Turbocharge Your React Application with shouldComponentUpdate and Immutable.js

Most of you have probably heard about how awesome React is, particularly when it comes to performance. The Virtual DOM is a great innovation in this regard, since it saves developers from having to deal with UI bottlenecks manually. However, sometimes speed is such a critical aspect that React's automatic DOM diffing isn't good enough. This is when shouldComponentUpdate comes into play.

So what is this magic method good for? It gives you fine-grained control over reconciliation of a component's DOM. If the method returns false, the component is considered clean, and therefore no diffing or rendering is performed. (Keep in mind that in this case all the component's children are considered clean as well.)

Let's say you have a list composed of many components with a complicated nested structure, and one item in the list changes. It would be a waste of resources to perform a diff on every single component in the list. By implementing shouldComponentUpdate, you can basically tell React to ignore all the components except the one that changed.

shouldComponentUpdate takes nextProps and nextState as arguments. It’s up to you to tell React if component needs to be rerendered to reflect the new properties and state. React would otherwise used a virtual DOM diff for this decision, which is quite a time-consuming operation. Ideally your shouldComponentUpdate should be as lightweight as possible, since it is called for every single component (unless the parent component returned false).

shouldComponentUpdate: function(nextProps, nextState) {  
    return nextProps.items !== this.props.items;
}

So how can immutable data structure help us with this issue? Consider this snippet:

var items = [{  
    item: 1,
    color: ‘#aeaeae’
},{
    item: 2,
    color: ‘#fefefe’
}];

items[0].color = ‘#ff0000’;

<Component items={items} />  

A naive implementation of shouldComponentUpdate (like the one given above) would produce the wrong result because, even though content of the array has changed, its reference remains the same. To fix it, you would have to compare the entire deep object structure (which is really ugly and slow).

By definition, immutable data structures never change. Any mutable operation returns a new instance of the structure, allowing us to easily compare two references.

const immutableList = Immutable.List.of(1, 10, 22);  
const modifiedList = immutableList.push(100);  
console.log(immutableList); // 1, 10, 22  
console.log(modifiedList); // 1, 10, 22, 100  

Pretty cool, eh? If the items in the previous shouldComponentUpdate example were immutable, we could safely compare the two references, and the implementation would suddenly start to work.

I prepared a benchmark repository on GitHub to demonstrate how the combination of immutable.js and React can improve application performance. It is basically just two large lists (10000 items each) of colored values. One list is mutable and the second uses an immutable data structure. React has handy performance tools to help you track down bottlenecks in your app. Using them is as simple as calling Perf.start() and Perf.stop(), which creates two reference checkpoints for performance calculations. Perf.printWasted() returns time that has been taken for components which didn’t actually need re-rendering. Here is the result:

Mutable data:
Mutable data

Immutable data:
Immutable data

As you can see, I managed to save 723ms (a nearly 4x performance boost) just by implementing an immutable data structure and shouldComponentUpdate. My conclusion is to always use immutable data structure for your properties and state to make sure your application will scale well if necessary. As a side benefit you can even get rid of lodash, since Immutable.js provides a rich API of utility functions for each collection type.

I strongly recommend reading the Advanced Performance section of the React docs (a real gem, by the way) for further explanation.

Tomas Weiss

Tomas Weiss

Full-stack JavaScript developer at Salsita