Skip to content

Commit 64c9a66

Browse files
author
Becca Bailey
authored
Merge pull request #2006 from FormidableLabs/improvement/memoize-reduce-children
Memoize getCalculatedProps
2 parents 038cd9b + a34d708 commit 64c9a66

File tree

4 files changed

+69
-5
lines changed

4 files changed

+69
-5
lines changed

packages/victory-group/src/helper-methods.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
/* eslint-disable no-use-before-define */
33
import { assign } from "lodash";
44
import React from "react";
5-
import { Helpers, Scale, Data, Wrapper } from "victory-core";
5+
import { Data, Helpers, Scale, Wrapper } from "victory-core";
6+
import isEqual from "react-fast-compare";
67

78
const fallbackProps = {
89
width: 450,
@@ -63,6 +64,37 @@ export function getCalculatedProps(props, childComponents) {
6364
};
6465
}
6566

67+
// We need to remove sharedEvents in order to memoize the calculated data
68+
// With shared events, the props change on every event, and every value is re-calculated
69+
const withoutSharedEvents = (props) => {
70+
const { children } = props;
71+
const modifiedChildren = React.Children.toArray(children).map((child) => {
72+
return {
73+
...child,
74+
props: Helpers.omit(child.props, ["sharedEvents"])
75+
};
76+
});
77+
props.children = modifiedChildren;
78+
return props;
79+
};
80+
81+
export function useMemoizedProps(initialProps) {
82+
const modifiedProps = withoutSharedEvents(initialProps);
83+
const [props, setProps] = React.useState(modifiedProps);
84+
85+
// React.memo uses shallow equality to compare objects. This way props
86+
// will only be re-calculated when they change.
87+
React.useEffect(() => {
88+
if (!isEqual(modifiedProps, props)) {
89+
setProps(modifiedProps);
90+
}
91+
}, [props, setProps, modifiedProps]);
92+
93+
return React.useMemo(() => {
94+
return getCalculatedProps(props, props.children);
95+
}, [props]);
96+
}
97+
6698
function pixelsToValue(props, axis, calculatedProps) {
6799
if (!props.offset) {
68100
return 0;

packages/victory-group/src/victory-group.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
useAnimationState
1212
} from "victory-core";
1313
import { VictorySharedEvents } from "victory-shared-events";
14-
import { getChildren, getCalculatedProps } from "./helper-methods";
14+
import { getChildren, useMemoizedProps } from "./helper-methods";
1515
import isEqual from "react-fast-compare";
1616

1717
const fallbackProps = {
@@ -44,7 +44,7 @@ const VictoryGroup = (initialProps) => {
4444
} = modifiedProps;
4545

4646
const childComponents = React.Children.toArray(modifiedProps.children);
47-
const calculatedProps = getCalculatedProps(modifiedProps, childComponents);
47+
const calculatedProps = useMemoizedProps(modifiedProps);
4848
const { domain, scale, style, origin } = calculatedProps;
4949

5050
const newChildren = React.useMemo(() => {

packages/victory-stack/src/helper-methods.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { assign, keys, orderBy } from "lodash";
44
import React from "react";
55
import { Helpers, Scale, Wrapper } from "victory-core";
6+
import isEqual from "react-fast-compare";
67

78
const fallbackProps = {
89
width: 450,
@@ -160,6 +161,37 @@ export function getCalculatedProps(props, childComponents) {
160161
};
161162
}
162163

164+
// We need to remove sharedEvents in order to memoize the calculated data
165+
// With shared events, the props change on every event, and every value is re-calculated
166+
const withoutSharedEvents = (props) => {
167+
const { children } = props;
168+
const modifiedChildren = React.Children.toArray(children).map((child) => {
169+
return {
170+
...child,
171+
props: Helpers.omit(child.props, ["sharedEvents"])
172+
};
173+
});
174+
props.children = modifiedChildren;
175+
return props;
176+
};
177+
178+
export function useMemoizedProps(initialProps) {
179+
const modifiedProps = withoutSharedEvents(initialProps);
180+
const [props, setProps] = React.useState(modifiedProps);
181+
182+
// React.memo uses shallow equality to compare objects. This way props
183+
// will only be re-calculated when they change.
184+
React.useEffect(() => {
185+
if (!isEqual(modifiedProps, props)) {
186+
setProps(modifiedProps);
187+
}
188+
}, [props, setProps, modifiedProps]);
189+
190+
return React.useMemo(() => {
191+
return getCalculatedProps(props, props.children);
192+
}, [props]);
193+
}
194+
163195
function getLabels(props, datasets, index) {
164196
if (!props.labels) {
165197
return undefined;

packages/victory-stack/src/victory-stack.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
usePreviousProps
1313
} from "victory-core";
1414
import { VictorySharedEvents } from "victory-shared-events";
15-
import { getChildren, getCalculatedProps } from "./helper-methods";
15+
import { getChildren, useMemoizedProps } from "./helper-methods";
1616
import isEqual from "react-fast-compare";
1717

1818
const fallbackProps = {
@@ -45,7 +45,7 @@ const VictoryStack = (initialProps) => {
4545
} = modifiedProps;
4646

4747
const childComponents = React.Children.toArray(modifiedProps.children);
48-
const calculatedProps = getCalculatedProps(modifiedProps, childComponents);
48+
const calculatedProps = useMemoizedProps(modifiedProps);
4949
const { domain, scale, style, origin } = calculatedProps;
5050

5151
const newChildren = React.useMemo(() => {

0 commit comments

Comments
 (0)