ashish@home:~$

Using Purecomponent To Prevent Wasted Renders In React

The shouldComponentUpdate lifecycle event in React components provides a way to have more granular control over when a component should re-render as a result of an internal state update, or as dictated by the parent. Earlier this could be done by comparing nextProps with the current props using the now deprecated ShallowCompare addon. React now has support for PureComponent to achieve the same thing.

Lets say you have a child component that simply displays a text prop passed to it:

1
2
3
4
5
6
7
8
9
10
11
import React, { Component } from 'react';

export default class FirstChild extends Component {  
  render() {
    return (
      <div>
        {this.props.text}
      </div>
    );
  }
}

And you have another child component that displays another text prop passed to it and logs “Rendering” to the console for each render cycle pass:

1
2
3
4
5
6
7
8
9
10
11
12
import React, { Component } from 'react';

export default class SecondChild extends Component {  
  render() {
    console.log("Rendering!");
    return (
      <div>
        {this.props.text}
      </div>
    );
  }
}

The common parent to these children updates a state text every 1 second and passes it as a prop to the first child:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React, { Component } from 'react';
import FirstChild from './FirstChild';
import SecondChild from './SecondChild';

export default class Parent extends Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      text: Math.random()
    };
  }
  
  componentDidMount() {
    setInterval(() => this.setState({ text: Math.random() }), 1000);
  }
  
  render() {
    return (
      <div>
        <FirstChild
          text={this.state.text}
        />  
        <SecondChild
          text="some random data"
        />
      </div>
    );
  }
}

This periodic state update in turn causes re-renders for both the child components. Ideally, second child should not be affected by the parent state update at all, since the prop passed remains the same. Console log by running this code:

Rendering!
Rendering!
Rendering!
...

This could be solved by using a PureComponent to implement the second child:

1
2
3
4
5
6
7
8
9
10
11
12
import React, { PureComponent } from 'react';

export default class SecondChild extends PureComponent {  
  render() {
    console.log("Rendering!");
    return (
      <div>
        {this.props.text}
      </div>
    );
  }
}

Console log with modified code:

Rendering!

Working example for this can be found here.

PureComponent does a shallow check on this.props and nextProps automatically to determine if it should update. Note that if the passed prop changes deeply, PureComponent will not be able to catch it. In that case you should do a deep equality check (maybe use underscorejs for it):

shouldComponentUpdate(nextProps, nextState) {
  return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
}