Skip to content

Commit

Permalink
Increasing performance by avoiding styles recalculations (#211)
Browse files Browse the repository at this point in the history
* Split DOM reads and writes to be performed in batches to avoid styles recalculations.

* Cosmetics
  • Loading branch information
egorshulga authored and joshwcomeau committed Jan 5, 2018
1 parent 34d455b commit 2a370e8
Showing 1 changed file with 30 additions and 6 deletions.
36 changes: 30 additions & 6 deletions src/FlipMove.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,14 @@ class FlipMove extends Component<ConvertedProps, FlipMoveState> {
this.doesChildNeedToBeAnimated,
);

dynamicChildren.forEach((child, n) => {
// Splitting DOM reads and writes to be peformed in batches
const childrenInitialStyles = dynamicChildren.map(child =>
this.computeInitialStyles(child),
);
dynamicChildren.forEach((child, index) => {
this.remainingAnimations += 1;
this.childrenToAnimate.push(getKey(child));
this.animateChild(child, n);
this.animateChild(child, index, childrenInitialStyles[index]);
});

if (typeof this.props.onStartAll === 'function') {
Expand Down Expand Up @@ -382,7 +386,7 @@ class FlipMove extends Component<ConvertedProps, FlipMoveState> {
});
}

animateChild(child: ChildData, index: number) {
animateChild(child: ChildData, index: number, childInitialStyles: Styles) {
const { domNode } = this.getChildData(getKey(child));
if (!domNode) {
return;
Expand All @@ -396,7 +400,7 @@ class FlipMove extends Component<ConvertedProps, FlipMoveState> {
// In FLIP terminology, this is the 'Invert' stage.
applyStylesToDOMNode({
domNode,
styles: this.computeInitialStyles(child),
styles: childInitialStyles,
});

// Start by invoking the onStart callback for this child.
Expand Down Expand Up @@ -553,19 +557,24 @@ class FlipMove extends Component<ConvertedProps, FlipMoveState> {

this.parentData.boundingBox = this.props.getPosition(parentDomNode);

// Splitting DOM reads and writes to be peformed in batches
const childrenBoundingBoxes = [];

this.state.children.forEach(child => {
const childKey = getKey(child);

// It is possible that a child does not have a `key` property;
// Ignore these children, they don't need to be moved.
if (!childKey) {
childrenBoundingBoxes.push(null);
return;
}

// In very rare circumstances, for reasons unknown, the ref is never
// populated for certain children. In this case, avoid doing this update.
// see: https://github.com/joshwcomeau/react-flip-move/pull/91
if (!this.hasChildData(childKey)) {
childrenBoundingBoxes.push(null);
return;
}

Expand All @@ -574,15 +583,30 @@ class FlipMove extends Component<ConvertedProps, FlipMoveState> {
// If the child element returns null, we need to avoid trying to
// account for it
if (!childData.domNode || !child) {
childrenBoundingBoxes.push(null);
return;
}

this.setChildData(childKey, {
boundingBox: getRelativeBoundingBox({
childrenBoundingBoxes.push(
getRelativeBoundingBox({
childDomNode: childData.domNode,
parentDomNode,
getPosition: this.props.getPosition,
}),
);
});

this.state.children.forEach((child, index) => {
const childKey = getKey(child);

const childBoundingBox = childrenBoundingBoxes[index];

if (!childKey) {
return;
}

this.setChildData(childKey, {
boundingBox: childBoundingBox,
});
});
}
Expand Down

0 comments on commit 2a370e8

Please sign in to comment.