
Memoization: React.memo, useMemo, useCallback
React’s memoization tools let you skip re-renders and recomputations when inputs haven’t changed. Use them deliberately — they add complexity and aren’t free.
React.memo
Wraps a component and skips re-rendering if props haven’t changed (shallow comparison):
const UserCard = React.memo(function UserCard({ user, onEdit }) {
return (
<div>
<span>{user.name}</span>
<button onClick={onEdit}>Edit</button>
</div>
);
}); UserCard won’t re-render when its parent re-renders, as long as user and onEdit are the same references.
The catch: if the parent creates onEdit inline, it’s a new function reference every render — memo does nothing:
// Breaks memo: new function reference on every parent render
<UserCard onEdit={() => handleEdit(user.id)} /> Fix with useCallback.
useCallback
Memoizes a function reference across renders:
const handleEdit = useCallback(() => {
editUser(user.id);
}, [user.id]);
<UserCard user={user} onEdit={handleEdit} /> handleEdit keeps the same reference as long as user.id doesn’t change. Now React.memo can bail out successfully.
Only memoize callbacks when:
- The function is passed to a memoized child (
React.memo) - The function is a dependency in
useEffectoruseMemo
useMemo
Memoizes a computed value:
const sortedItems = useMemo(() => {
return items.slice().sort((a, b) => a.date - b.date);
}, [items]); Recomputes only when items changes. Useful when the computation is expensive and the component re-renders frequently for unrelated reasons.
Measuring “expensive”: wrap in console.time. If the computation takes under ~1ms, useMemo overhead may outweigh the savings.
Custom Equality in React.memo
The second argument defines custom comparison logic:
const Chart = React.memo(
function Chart({ data, config }) { ... },
(prevProps, nextProps) => {
return prevProps.data.id === nextProps.data.id; // ignore config changes
}
); Return true to skip re-render, false to re-render.
Referential Stability
The whole system depends on stable references. Objects and arrays are new references every render unless memoized:
// Bad: new object every render — breaks memo downstream
<Chart config={{ color: 'blue', width: 2 }} />
// Good: stable reference
const config = useMemo(() => ({ color: 'blue', width: 2 }), []);
<Chart config={config} /> When to Add Memoization
Profile first. Add memoization when:
- A component re-renders visibly too often (check React DevTools Profiler)
- A computation takes >1ms and runs on every render
- A callback is passed to a memoized child that would otherwise re-render
Don’t add it preemptively — it makes code harder to read and can mask real performance issues.









