June 30, 2023 · 2 min read
When to Use memo in React
When state changes, React re-renders that component and every one of its descendants, regardless of whether their props actually changed. Most of the time this is fine — React's diffing is fast, and re-rendering isn't the same as re-painting the DOM. But in components with expensive render logic, or components rendered many times in a list, that repeated work adds up.
React.memo wraps a component so React skips re-rendering it when its props are shallow-equal to the previous render. It's a targeted fix, not a default.
When it actually helps
- Pure presentational components rendered in a large list — a row component re-rendered hundreds of times when only one row's data changed.
- Components with expensive render work — heavy computation, large DOM trees, or chart/canvas rendering that shouldn't repeat every parent re-render.
- Components that receive the same props most of the time — a sidebar or header that re-renders whenever an unrelated part of the page updates state.
When it doesn't help — or actively hurts
- Components that receive new object/array/function props on every render.
memodoes a shallow comparison; a new inline() => {}or{ ...obj }literal breaks it every time. You needuseCallback/useMemoon the parent too, ormemodoes nothing but add a comparison cost. - Components that almost always re-render anyway because their props change on most renders — the comparison is pure overhead.
- Cheap components. If a component renders a
<span>and some text, the cost of the shallow-equality check can exceed the cost of just re-rendering it.
A simple mental model
Reach for memo when you can point at a specific, measured re-render that's expensive and unnecessary — not as a blanket wrapper around every component. Profile first (React DevTools' Profiler tab), memoize second.
const Row = memo(function Row({ item }: { item: Item }) {
return <li>{item.label}</li>;
});
If Row is rendered inside a list and the parent re-renders for unrelated reasons, wrapping it in memo stops React from re-rendering every row — as long as item itself doesn't change identity on every render.
An earlier take on this topic is also up on dev.to.