Skip to content

Instantly share code, notes, and snippets.

@brigand
Last active April 27, 2016 14:30
Show Gist options
  • Save brigand/e31768cbdf8eca67b09b225301a46e09 to your computer and use it in GitHub Desktop.
Save brigand/e31768cbdf8eca67b09b225301a46e09 to your computer and use it in GitHub Desktop.
updateIfChanged

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.

Implementation

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} />;
  }
}

Simple Example

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.

Advanced Example

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>
));

TODO

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment