By Dmitry Vasyuk January 11, 2017 4:20 PM
Why and How to Use PureComponent in React.js

React 15.3 was released on June 29, 2016 and the first item announced in the release notes was the support for React.PureComponent, which replaces its predecessor pure-render-mixin. In this article we’re going to discuss why this component is so important and where we could use it.

React.PureComponent is one of the most significant ways to optimize React applications that is easy and fast to implement. The usage of React.PureComponent gives a considerable increase in performance because it reduces the number of render operation in the application.

react-render-explanation

PureComponent changes the life-cycle method shouldComponentUpdate and adds some logic to automatically check whether a re-render is required for the component. This allows a PureComponent to call method render only if it detects changes in state or props, hence, one can change the state in many components without having to write extra checks like:

if (this.state.someVal !== computedVal) {
  this.setState({ someVal: computedVal })
}

In the source code of React, in case when a component is a “Pure” one, the following comparison is done:

if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}

The usage of the method shallowEqual tells us that only a shallow check of props and state will be made, which means that deeply nested objects and arrays will not be compared.

Deep comparison is a very expensive operation and if a PureComponent used it, it would do more harm than good. Additionally, you can use the proven method shouldComponentUpdate to manually determine the necessity of a new re-render. The easiest option is a direct comparison of parameters:

shouldComponentUpdate(nextProps, nextState) {
  return nextProps.user.id === props.user.id;
}

Alternatively, you can use immutable attributes. Comparisons in this case become very simple, because the available variables don’t get changed and new ones are always created. Libraries like Immutable.js is our faithful ally in this case.

The usage

PureComponent saves us time and helps us to avoid writing redundant code. It is important to understand the right ways to use it, otherwise its usefulness is voided. Since the PureComponent does shallow comparison, changes inside props or state will be ignored. For example, let’s consider a situation where the parent component has a render method and a click handler:

handleClick() {
  let {items} = this.state

  items.push('new-item')
  this.setState({ items })
}

render() {
  return (
    <div>
      <button onClick={::this.handleClick} />
      <ItemList items={this.state.items} />
    </div>
  )
}

If ItemList was a PureComponent, it would not re-rerender because the this.state.items is still pointing to the same object, albeit with the new content inside. However, this case could be easily corrected by removing the mutations, for example:

handleClick() {
  this.setState(prevState => ({
    words: prevState.items.concat(['new-item'])
  }));
}

A PureComponent will always re-render if the state or props reference a new object. This implies that if we do not want to lose the benefits of PureComponent, we should avoid such structures:

<Entity values={this.props.values || []}/>

The new array, even though it is empty, will always force the component to re-render. To avoid this problem you can use defaultProps, that contains the initial empty state of a property. Another way to solve the problem is to use a code like this:

<CustomInput onChange={e => this.props.update(e.target.value)} />

During the creation of this component, a new instance of the function is produced, so the PureComponent gets new data and triggers a re-render. The simplest way to fix this, is to use bind in the component’s constructor.

constructor(props) {
    super(props)
    this.update = this.update.bind(this)
}
update(e) {
    this.props.update(e.target.value)
}
render() {
    return <MyInput onChange={this.update} />
}

Also, any component that contains child elements created in JSX, will always return false for shallowEqual checks.

It is important to remember, that PureComponent skips the re-render operation not only for component itself, but also for all its children, so the best use case for PureComponent are presentational components which have no child components and no dependencies on the global state in the application.

Conclusions

In fact, the transition to PureComponent is quite simple, if you are aware of peculiarities of the shallowEqual and JS itself. Normally, the migration as simple as changing the base class from

class MyComponent extends Component {...}

to

class MyComponent extends PureComponent {...}

The original implementation continues to work smoothly and even with increased performance. So, I definitely recommend everyone to try it and use it in the apps.

Caution

When a pure component has children, all child components that depend on a change in this.context will not re-render when the context is changed, unless the parent PureComponent declares contextTypes.

This is a translation of the article which was first published at habrahabr.