Skip to content

Commit

Permalink
Preventing unnecessary AnimateInOut re-renderings
Browse files Browse the repository at this point in the history
  • Loading branch information
nfantone committed Oct 23, 2020
1 parent ec06fa2 commit 9073656
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 45 deletions.
81 changes: 46 additions & 35 deletions src/view/animate-in-out/animate-in-out.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,44 @@ type State = {|
animate: InOutAnimationMode,
|};

const getNextStateFromProps = (props: Props, state: State): State => {
if (!props.shouldAnimate) {
return {
isVisible: Boolean(props.on),
data: props.on,
animate: 'none',
};
}

// need to animate in
if (props.on) {
return {
isVisible: true,
// have new data to animate in with
data: props.on,
animate: 'open',
};
}

// need to animate out if there was data

if (state.isVisible) {
return {
isVisible: true,
// use old data for animating out
data: state.data,
animate: 'close',
};
}

// close animation no longer visible
return {
isVisible: false,
animate: 'close',
data: null,
};
};

// Using a class here rather than hooks because
// getDerivedStateFromProps results in far less renders.
// Using hooks to implement this was quite messy and resulted in lots of additional renders
Expand All @@ -32,42 +70,15 @@ export default class AnimateInOut extends React.PureComponent<Props, State> {
animate: this.props.shouldAnimate && this.props.on ? 'open' : 'none',
};

static getDerivedStateFromProps(props: Props, state: State): State {
if (!props.shouldAnimate) {
return {
isVisible: Boolean(props.on),
data: props.on,
animate: 'none',
};
}

// need to animate in
if (props.on) {
return {
isVisible: true,
// have new data to animate in with
data: props.on,
animate: 'open',
};
}

// need to animate out if there was data
static getDerivedStateFromProps(props: Props, state: State): ?State {
const nextState = getNextStateFromProps(props, state);
const shouldUpdate =
state.isVisible !== nextState.isVisible ||
state.data !== nextState.data ||
state.animate !== nextState.animate;

if (state.isVisible) {
return {
isVisible: true,
// use old data for animating out
data: state.data,
animate: 'close',
};
}

// close animation no longer visible
return {
isVisible: false,
animate: 'close',
data: null,
};
// Avoid re-rendering component if not needed
return shouldUpdate ? nextState : null;
}

onClose = () => {
Expand Down
25 changes: 15 additions & 10 deletions src/view/droppable/droppable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,26 @@ export default function Droppable(props: Props) {
getDroppableRef,
});

const renderPlaceholder = useCallback(
({ onClose, data, animate }: AnimateProvided) => (
<Placeholder
placeholder={(data: any)}
onClose={onClose}
innerRef={setPlaceholderRef}
animate={animate}
contextId={contextId}
onTransitionEnd={onPlaceholderTransitionEnd}
/>
),
[contextId, onPlaceholderTransitionEnd, setPlaceholderRef],
);

const placeholder: Node = (
<AnimateInOut
on={props.placeholder}
shouldAnimate={props.shouldAnimatePlaceholder}
>
{({ onClose, data, animate }: AnimateProvided) => (
<Placeholder
placeholder={(data: any)}
onClose={onClose}
innerRef={setPlaceholderRef}
animate={animate}
contextId={contextId}
onTransitionEnd={onPlaceholderTransitionEnd}
/>
)}
{renderPlaceholder}
</AnimateInOut>
);

Expand Down

0 comments on commit 9073656

Please sign in to comment.