React.memo
React.memo
React.memo is a Higher-Order Component (HOC) used to optimize performance by preventing unnecessary re-renders of functional components.
Default Rendering Behavior
By default in React, if a parent component re-renders, all of its children will re-render, regardless of whether their props changed.
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
{/* Even though Child gets no props, it re-renders every time count changes! */}
<Child />
</div>
);
}
Using React.memo
If the <Child /> component contains a complex UI (like a large graph or a heavy table), those unnecessary re-renders will cause performance lag.
You can fix this by wrapping the component in React.memo().
import { memo } from 'react';
const Child = memo(function Child(props) {
// Heavy rendering logic here...
return <div>I only re-render if my props change!</div>;
});
When you wrap a component in memo, React will compare the new props with the old props before attempting to re-render.
- If the props are exactly the same, React skips the render phase for that component and reuses the last rendered result.
- If the props have changed, the component re-renders normally.
The Referential Equality Trap
React.memo does a shallow comparison of props using Object.is.
This means it works perfectly for primitive props (strings, numbers, booleans). However, if you pass an object, array, or function as a prop, React.memo will often fail to optimize anything, because a new reference is created on every render of the parent.
function Parent() {
const [count, setCount] = useState(0);
// ❌ BAD: This object gets a brand new memory address on every render.
// React.memo will think the props changed, and re-render the child anyway!
const config = { theme: 'dark' };
// ❌ BAD: This function gets a new memory address on every render.
const handleClick = () => console.log('Clicked');
return (
<MemoizedChild config={config} onClick={handleClick} />
);
}
To make React.memo work correctly with objects and functions, you must combine it with useMemo and useCallback in the parent component to stabilize those references.
Custom Comparison Function
If a shallow comparison isn’t enough, React.memo accepts a second argument: a custom comparison function. It works similarly to shouldComponentUpdate in old class components, but the logic is reversed (it returns true if props are equal).
const MemoizedComponent = memo(MyComponent, (prevProps, nextProps) => {
// Return true if passing nextProps to render would return
// the same result as passing prevProps to render,
// otherwise return false
return prevProps.user.id === nextProps.user.id;
});
Interview Questions
Q: What does React.memo do?
A: It is a Higher-Order Component (HOC) that prevents a functional component from re-rendering if its incoming props have not changed. It does this by performing a shallow equality comparison on the previous props versus the new props.