React — When Should Pure Components Be Used?

React.memo / React.PureComponent usage guide

What’s a Pure Component?

(Official docs: React.PureComponent, React.memo)

In the following example, when App re-renders,
<Main userName={userName}/> and <Header/> are called:

And they always return new React Elements.

Remember that JSX is just syntax sugar for React.createElement (in React 17 it’s equivalent mostly conceptually) so App‘s code is equivalent to:

Usually, as a result of creating React Elements, they also re-render, so their code will run.

If they are pure, however, they only re-render when any of their props change, otherwise React re-uses the last rendered result.

In our case, a pure Header would never re-render because it has no props.

A pure Main will only re-render if userName changes:
prevProp.userName !== nextProp.userName

So when userName doesn’t change, Main’s last result is re-used and its code, including heavyCalculation doesn’t run.

Sandbox

In the following sandbox, I used an awesome library by a friend of mine, Nir Avraham: use-header-scroll.

The library’s hook returns different header sizes as you scroll resulting in a neat effect where the scroll hides and reveals itself as you scroll.

Try scrolling when Main is pure and isn’t pure. It can be changed using the checkbox in the middle of the header.

Scroll Performance

In the sandbox above, you probably noticed how scrolling was laggy when Main was not pure.

Profiler For The Rescue!

When you encounter a React performance issue, try recording the scenario with the React Profiler. In our case:

  1. Make sure Main is not pure in order to prepare the performance issue scenario
  2. Start recording
  3. Scroll (reproduce the performance issue)
  4. Stop the recording

After that, your profiler should look like this:

React profiler screenshot where Main re-renders often
React profiler screenshot where Main re-renders often

Now we can see that the issue is that Main is being called on every single scroll.

We can also see a similar issue using Chrome Performance Profiler:

The performance issue happens because React has to run the relatively huge block of code in Main on every single scroll event before it could paint the updated App results on the screen (Main and Header).

The time difference between scrolling events and Header’s height changes paintings on the screen makes it feel laggy.

Preventing The Heavy Render

When Main is pure, however, it is rendered only once, and React doesn’t need to run its relatively heavy code on each scroll event.

Then Why Not Make All React Components Pure?

For more details see the following GitHub issue on Facebook’s repo:
Q: When should you NOT use React memo?

In short, it’s because React.memo has a cost in terms of memory and CPU because it has to shallowly compare previous props to the next props.

This sounds like not a big deal, but it is for bigger applications and considered a deal big enough to decide manually what has to be pure and what isn’t.

It seems like, in most cases, making React components pure will be redundant, because they will always re-render anyway.

For example, in the following cases, each of the props: onClick, style, children, and items are generated inline and will be a new prop on each render:

So if Main always re-renders, why would you compare its props on each render?

Optimizations To Help Keeping Components Pure

In all the cases mentioned in the example above, you can memoize props using useCallback, useMemo, or techniques like extracting prop definitions out of the component, and more.

But these optimizations too, have their own costs in terms of resources and code readability.

For example, this is how App from the example above might look like to make sure Main won’t always re-render (unless one of the props changes):

As you can see the code is more complicated, less readable, more prone to bugs, and costs resources to memoize in of itself (the hooks in lines 14–16 cost resources).

Does it worth it? You tell me. (After using the profiler 😉)

So, When Should Pure Components in React be Used Indeed?

Let’s narrow down the considerations in hand to create a rule of thumb:

Considerations:

  • Making a component pure, forces React to compare props before re-rendering the component. (prevProp !== newProp)
  • A React Component, especially a bigger one, is relatively expensive to render.
  • A component with inline generated props will always re-render (like style={{width: 100%}}.
  • Optimizations to inline generated props to make sure a component doesn’t always re-render has its overheads (code readability, resources).

Rule of thumb:

A component should be made pure if its render causes a performance issue and the render can be prevented by making the component pure.

Good candidates for components that cause performance issues which can be solved by making these components pure are usually:

  • Not small. (has considerable calculations or generates many react elements inside)
  • Re-render on user interaction. (click, scroll, hover)

Thanks :)