React has taken the web development community by a storm, and with it functional programming concepts have embedded themselves in the mainstream. One common statement you will often read is that all state in React should be immutable, and this practice is justified as necessary for performance reasons. This statement is entirely true, but it only tells half the truth. Immutability alone will not yield any performance gains in React (it’ll actually make things slower).
You can reap the gains from immutable state in React if you inherit from React.PureComponent instead of
So why does the above code work? That requires a little bit of knowledge about the rendering process.
render function returns a tree of React elements, also known as the virtual DOM. This data structure is a representation of the browser’s DOM that React can manipulate in a performant way. In essence, it’s a 1-1 mapping to the browser DOM.
Whenever state or props change, instead of updating the browser DOM directly, React will call the
render function of the updated component, getting its new virtual DOM, and it will compare that virtual DOM to the previous render tree (aka the old virtual DOM). This comparison process (known as reconciliation) allows it to identify the minimum set of changes that need to be made to the browser DOM, allowing for for massive performance gains. If the two trees are identical, then no changes need to be made to the UI.
setState is called on a component, that component (and all of the components it renders) go through a two step process for React determine if the UI should update.
Before the virtual DOM is rendered, React first runs a component’s shouldComponentUpdate lifecycle method
It is your choice to implement this method. It takes as parameters the new state and props and should return a boolean indicating whether the component should re-render. React will only proceed with re-rendering if you return
true from this function. By default,
shouldComponentUpdate always returns
Let’s look at a simple example
Counter will only re-render if
state.counter changes. Suppose we remove the
shouldComponentUpdate above, is there another circumstance where
Counter‘ re-renders? It would re-render if a component that renders
Counter re-renders! It does not matter that
Counter‘s render function does not depend on props!
true, React begins the second part of the re-render process, the part where it invokes the render function, generating its virtual DOM. It will then compare that render tree to the old virtual DOM. In the scenario above, these two trees will always be identical (after all, the component doesn’t even have a way to change state), and the entire operation will a be waste CPU cycles.
false, we inform React that a re-render will not be necessary, sparing it an unnecessary to call a component’s render function and the comparison of the render trees.
shouldComponentUpdate is sometimes a tedious task, and React supplies a helper to handle a very common
shouldComponentUpdate scenario. I mentioned
React.pureComponent is a handy class that we can inherit from that implements
shouldComponentUpdate as a shallow comparison across the old props and new props (or the old state and new state). If there are no shallow differences between these objects, then
shouldComponentUpdate will return false, and the virtual DOM’s re-rendering process can be skipped entirely, resulting in potentially enormous performance savings.
Immutability matters because
React.pureComponent is going to do shallow comparisons against the prop and state objects. And, as you hopefully know, a shallow comparison’s referential equality checks will only yield the expected results if immutability is respected.
If you are not properly respecting immutability, then you are going to run into scenarios where your components fail to re-render when they actually should. A failure to respect immutability will cause a shallow comparison to return false (meaning
shouldComponentUpdate returns false) because the old props and new props (or the old state and new state) reference the same object in memory, despite the fact that props/state changed.
When used carefully however,
React.pureComponent can yield enormous performance improvements at almost no cost. All you need to do is inherit your components from
React.pureComponent instead of
React.Component and respect immutability when changing state.
I hope you found this article helpful. Please feel free to email me to reach out if you have questions.