Pure Components
In the last lesson, we saw how a component will automatically re-render when its parent re-renders, regardless of whether or not its props have changed.
In larger applications, this can lead to performance problems. A single state change might re-render dozens or even hundreds of components, even if only a small fraction of them actually need to re-render.
Fortunately, React provides an escape hatch we can use: the React.memo
utility.
Here's what it looks like:
function Decoration() { return ( <div className="decoration"> ⛵️ </div> );}
const PureDecoration = React.memo(Decoration);
export default PureDecoration;
React.memo
is a utility that lets us tell React: “Hey, this component is pure! It doesn't need to re-render unless its props or state changes. It will always return the exact same UI when given the same props + state”.
It takes a component as an argument (Decoration
) and augments it, giving it a new super-power: it can selectively ignore re-renders that don't affect it.
When the parent component re-renders, React will try to re-render the child PureDecoration
, but PureDecoration
steps in and says “None of my props have changed, and so I won't be re-rendering this time.”
This uses a technique known as memoization.
It's missing the R, but we can sorta think of it as “memorization”. The idea is that React will remember the previous snapshot. If none of the props have changed, React will re-use that stale snapshot rather than going through the trouble of generating a brand new one.
Let's suppose I wrap both BigCountNumber
and Decoration
with the React.memo
helper. Here's how this would affect the renders:
App
Counter
count: 0
BigCountNumber
Props: { count }
Pure Component
Decoration
Pure Component
When count
changes, we re-render Counter
, and React will try to render both descendant components.
Because BigCountNumber
takes count
as a prop, and because that prop has changed, BigCountNumber
is re-rendered. But because Decoration
's props haven't changed (on account of it not having any), the original snapshot is used instead.
I like to pretend that React.memo
is a bit like a lazy photographer. If you ask it to take 5 photos of the exact same thing, it'll take 1 photo and give you 5 copies of it. The photographer will only snap a new picture when your instructions change.
Here's a live-code version, if you'd like to poke at it yourself. Each memoized component has a console.info
call added, so you can see in the console exactly when each component renders:
Code Playground
- BigCountNumber render
- Decoration render
To summarize:
- The only way to re-render anything in React is to update a state variable by calling a state-setter function (eg.
setCount
). - When a component re-renders, it automatically re-renders all of its descendants, even if none of their props have changed.
- We can wrap our component with
React.memo
to optimize it, so that it only re-renders if at least 1 of its props have changed since the last render.