With the introduction of the React hooks, we are using function components more, and what you might have noticed that they render all the time, not caring if the props stay the same. For example, if you have a big list of to-do items, and you are adding a new item to the list, all the item components will re-render, even if they return the same result given the same props.
Class components have additional methods to prevent unnecessary rendering: PureComponent
and shouldComponentUpdate
. The former one is easy to use; the user doesn't need to write any additional logic; the component shallowly checks the props and rerenders if it detects new ones. The latter method is of the lifecycle, and the user needs to write the logic by manually comparing the properties or state, and tell the components if it has to render.
Function components have a method to avoid unnecessary rendering too, and only the syntax differs a little bit. React.memo() - does component memoization, cashing result and reuse it if the props did not change.
React.memo()
A higher-order component that will memoize your component, very similar to PureComponent
. It will shallowly compare current and new props of the component, and if nothing changes, React will skip the rendering of that component. To help method be effective as possible, make the prop structure as primitive values or simple objects. If you have a complex object, the comparison might fail, the component will not be memoized and will render again.
The use is pretty simple:
function List() {
return ['one', 'two'].map((item) => (
<MemoizedListItem item={item} />
);
}
// Memoize ListItem component
const MemoizedListItem = React.memo(function ListItem({ item }) {
return <li>{item}</li>
})
In this case, if you add a new item to the list, only the component with the new item will render to the DOM, the first two will use the result from the last rendering.
If the prop structure is more complex and requires manual comparison, you can pass an additional function to React.memo()
as a second argument. The function will receive two arguments prevProps
, nextProps
and it must return a boolean if the props are the same. It is the opposite return statement comparing with the shouldComponentUpdate
.
const ListItem = React.memo(
function ListItem({ item }) { ... }, // first argument
function arePropsEqual(prevProps, nextProps) { // second argument
return prevProps.item === nextProps.item;
}
);
Conclusion
React.memo()
is a performance optimization method, so first make sure if you need it. Do profiling of your app, measure the performance, and apply the method if it is necessary. A wrong application might lead to bugs.
Top comments (2)
I'm currently testing this on a project. I've wrapped some components with
memo(...)
(with only one parameter).Now I'm wondering: Is there a reason not to wrap a component with
memo()
? So far I don't see any change in the behavior of my app, except the devtools don't show those components as re-rendering if their content doesn't change. Am I missing something?After some research I found one reason to not
memo
a component: If its props change a lot (i.e. very frequently). But states aren't affected by this, because a state change (setSomeState()
, coming fromuseState()
) triggers a re-render in any case. So, examples would be<Clock>
or<Timer>
components, if the time to display gets passed to those components through props.Now, if I look at a typical admin-like application I barely see any of such components. Maybe different kind of applications have more of them and it makes more sense to not
memo
some or many components.Well, this is what I could find out so far und how I interpret that information. Please correct me, if I'm wrong or misinterpreting something.
I think, it's also worth mentioning what "render" actually means: A "render" is "just" a call of the component function. After this "render" React diffs the returned VDom with the previous VDom and applies changes to the DOM if necessary. I.e. a React render is not a DOM render. This is what made it feel scary for me before I knew the difference.