Skip to content
This repository has been archived by the owner on Feb 19, 2022. It is now read-only.

Experiment/scu #272

Merged
merged 15 commits into from
Jul 24, 2017
Merged

Experiment/scu #272

merged 15 commits into from
Jul 24, 2017

Conversation

boygirl
Copy link
Contributor

@boygirl boygirl commented Jul 23, 2017

cc/ @chrisbolin

This was the most fool-proof way I could figure out to prevent unnecessary re-renders...

sCU logic is roughly:
animating ? true
state changes* ? true
props changes* ? true
else false

state changes:
calculates all the aggregate state changes that could affect a given component in sCU and compares them to the existing cached state changes in a deep equality check. State changes on the parent namespace are ignored when the component is not standalone.

props changes:
prior to a deep equality check on props, and functions are removed. We were seeing unnecessary re-renders because isEqual doesn't support checking functions, and always returns false when comparing functions.

This change requires adding options to non-standard Victory components (i.e. axes and continuous components like VictoryArea) so that the state check know which types / indices to check i.e. "data", "labels", "tickLabels" etc.
See: FormidableLabs/victory-chart#500 (whoa, 500!)

This change is still slower than your WIP branch, but hits all the edge cases that were missing, specifically updating parent states (i.e. stacked bar example in victory voronoi container demo). I'm seeing what can be done to speed it up.

@boygirl boygirl requested a review from chrisbolin July 23, 2017 19:39
@chrisbolin
Copy link
Contributor

awesome. from what I can see it is now functionally correct (unlike my attempt).

but you're right, the heavily lifting is still quite heavy. this.getCalculatedValues(nextProps) seems to be the biggest burden in my testing. I am guessing it does some "work" that scales with data size.

Are we copying props.data? If so, not only would the copy take a long time, but the equality check will take much longer. This is because isEqual can "cheat" when comparing two large arrays by first seeing if they are simply references to the same array in memory. If that doesn't work, it will have to iterate through every element. Usually this 2nd process doesn't take too long, because it probably uses some good heuristics (like check last element, check length, etc). But if you copy an identical array and then compare it to its source, it will take a long time to prove its equality.

@chrisbolin
Copy link
Contributor

cacheValues is very clever -- reuse the values calculated in sCU in the render. awesome :)

@boygirl
Copy link
Contributor Author

boygirl commented Jul 24, 2017

@chrisbolin it's still a noticeable improvement over master for large datasets. I'm not sure how to get around the slow step.

@boygirl boygirl merged commit 79e61d9 into master Jul 24, 2017
@boygirl boygirl deleted the experiment/scu branch July 24, 2017 19:50
{ name: "parent", index: "parent" }, { name: "data" }, { name: "labels" }
];

const areVictoryPropsEqual = (a, b) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this should have a ton of unit tests behind it. A recursive equality check is something that it's so easy to have incorrect edge cases with.

Perhaps ticket out for "soon" if not now?

/cc @boygirl @chrisbolin

return undefined;
} else {
return component.index !== undefined ?
getState(component.index, component.name) :
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: indentation

// don't check for changes on parent props for non-standalone components
return undefined;
} else {
return component.index !== undefined ?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: typeof FOO === "undefined" or _.isUndefined()

this.hasEvents = props.events || props.sharedEvents || this.componentEvents;
this.events = this.getAllEvents(props);
const baseProps = this.getBaseProps(props, getSharedEventState);
const dataKeys = Object.keys(baseProps).filter((key) => key !== "parent");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're importing lodash _.keys elsewhere. Perhaps decide on one canonical way to do it? (Unless there's some reason for _.keys above that Object.keys can't do).

const sharedParentState = this.getSharedEventState("parent", "parent");
cacheValues(obj) {
keys(obj).forEach((key) => {
this[key] = obj[key];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any collision protections? It seems like you're adding straight to the underlying object. E.g., if someone passes an object with:

{ cacheValues: "foo" }

it would blow away this version method of this.cacheValues()

@@ -125,7 +125,7 @@ export default {
const mutationKeys = getKeys(childName);
return Array.isArray(mutationKeys) ?
mutationKeys.reduce((memo, key) => {
return assign(memo, getMutationObject(key, childName));
return assign({}, memo, getMutationObject(key, childName));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why aren't we mutating memo? Seems like extra work to copy all the props for no benefit (?)

@@ -134,7 +134,7 @@ export default {
const allChildNames = childNames === "all" ?
without(Object.keys(baseProps), "parent") : childNames;
return Array.isArray(allChildNames) ? allChildNames.reduce((memo, childName) => {
return assign(memo, getReturnByChild(childName));
return assign({}, memo, getReturnByChild(childName));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

@boygirl boygirl mentioned this pull request Jul 24, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants