updateIfChanged
is a pattern that allows preventing updates based on a unique key.
It inverts control of updates from a dumb child to the smarter parent. It's particularly useful because
it can avoid the normal issue of children and callbacks causing a rerender when it's not truly required.
The alternative workaround is usually memoizing the children and callbacks which can be expensive, complicated, and leak memory.
import deepEqual from 'lodash/isEqual';
export default class Box extends React.Component {
shouldComponentUpdate(nextProps) {
if (nextProps.updateIfChanged === undefined) {
return true;
}
return !deepEqual(this.props.updateIfChanged, nextProps.updateIfChanged);
}
render(){
var {updateIfChanged, ...props} = this.props;
return <div {...props} />;
}
}
To use it you would then render such a component and pass a unique key. Here's the simplest example:
<Box updateIfChanged={name}>
<span>Hello, {name}!</span>
</Box>
Without updateIfChanged
this component would always have to diff the span.
With it, all it does is check if name is the same as the previous render. With more complex children, especially when
there are more components included, this can be a significant performance boost.
React and Box have no way of knowing in which ways render is dynamic; however looking at the code doing the rendering you can clearly tell that the only dynamic part is the name
. This is why updateIfChanged
is a powerful tool.
Sometimes we have more moving parts than a single string. Due to using a deep equality check on updateIfChanged
an array or object can be passed. This is also very useful in lists where callbacks can't be bound once in the constructor.
{this.props.items.map((item) => (
<Box key={item.id} onClick={() => {}} updateIfChanged={[item.id, item.name, item.user]}>
<OtherComponent user={item.user} name={item.name} />
</Box>
));
Verify that updates OtherComponent doesn't update in the above case.
Do some benchmarks, maybe using react-perf to see in what situations this improves performance, and by what ammount.