React memo vs useMemo vs useCallback — When to Use Which
Three React APIs that all say 'memoize' — but they do different things. Clear explanations, examples, and a cheatsheet.
React memo vs useMemo vs useCallback
All three are about skipping unnecessary work. But they work differently.
🧠 The Core Idea
React re-renders a component whenever its parent re-renders — even if nothing changed. Memoization lets you say:
"Only redo this if the inputs actually changed."
1️⃣ React.memo — Skip re-rendering a component
Wraps a component. Skips re-render if props didn't change.
const Button = React.memo(({ onClick, label }: { onClick: () => void; label: string }) => {
console.log("Button rendered");
return <button onClick={onClick}>{label}</button>;
});
Without memo: Button re-renders every time the parent does.
With memo: Button skips re-render if onClick and label are the same.
2️⃣ useMemo — Cache a computed value
Memoizes the result of an expensive calculation.
const total = useMemo(() => {
return items.reduce((sum, item) => sum + item.price, 0);
}, [items]);
- Runs only when
itemschanges. - Returns the cached value on other renders.
❌ Without useMemo: recalculates on every render.
✅ With useMemo: recalculates only when items changes.
3️⃣ useCallback — Cache a function
Memoizes a function reference so it stays stable across renders.
const handleClick = useCallback(() => {
console.log("clicked", id);
}, [id]);
Without useCallback, a new function is created every render — which breaks React.memo on child components.
🔗 How They Work Together
React.memo compares props with ===. Functions and objects always fail === unless their reference is stable.
// ❌ Button re-renders every time — new function each render
<Button onClick={() => doSomething(id)} label="Click" />
// ✅ Button skips re-render — stable reference
const handleClick = useCallback(() => doSomething(id), [id]);
<Button onClick={handleClick} label="Click" />
⚠️ When NOT to Use Them
Memoization has a cost — it takes memory and adds complexity. Don't use it by default.
| Situation | Use memoization? |
|---|---|
| Cheap calculation | ❌ Not worth it |
| Component renders rarely | ❌ Skip it |
Heavy computation (sort, filter on large arrays) | ✅ Yes |
| Passing callbacks to memoized children | ✅ Yes |
| Passing objects/arrays as props to memoized children | ✅ Yes |
🧪 Quick Example: All Three Together
const ProductList = React.memo(({ items, onSelect }: Props) => {
// useMemo: only recalculate when items change
const total = useMemo(
() => items.reduce((s, i) => s + i.price, 0),
[items]
);
return (
<div>
<p>Total: ${total}</p>
{items.map((item) => (
<ProductCard key={item.id} item={item} onSelect={onSelect} />
))}
</div>
);
});
function App() {
const [cart, setCart] = useState([]);
// useCallback: stable reference so ProductList doesn't re-render
const handleSelect = useCallback(
(item) => setCart((prev) => [...prev, item]),
[]
);
return <ProductList items={products} onSelect={handleSelect} />;
}
✅ Summary
| API | Wraps | Skips |
|---|---|---|
React.memo | Component | Re-render when props unchanged |
useMemo | Computed value | Recalculation when deps unchanged |
useCallback | Function | Recreation when deps unchanged |
✨ Final Takeaway
React.memostops a component from re-rendering.useMemostops a value from recalculating.useCallbackstops a function from being recreated. Use them together —memoneedsuseCallbackto actually work.
You made it to the end!
Did this help? Leave a reaction — it takes one second.
Got feedback? 💬
Typo, suggestion, question — I read every message.
Writing bite-sized JS, React & Next.js tips
Related
Get new posts in your inbox
No spam. Unsubscribe any time.