⚡ React Performance — memo, useMemo and useCallback Explained
Stop unnecessary re-renders in React. Learn React.memo, useMemo and useCallback — when they help, when they don't, and how to measure with React DevTools profiler.
The biggest React performance trap: re-rendering more than needed. Three tools help:
React.memo— memoize a component (skip render if props unchanged)useMemo— memoize a computed valueuseCallback— memoize a function reference
⚠️ Reality check first
React is fast. Profile before optimizing. Most apps don't need any of these. Premature memoization clutters code and sometimes makes things slower.
🟢 React.memo
const Row = React.memo(function Row({ user }) {
return <li>{user.name}</li>;
});Row skips re-render if user reference is the same. Use shallow equality by default; pass a custom comparator for objects.
🟣 useMemo — cache expensive computations
const filtered = useMemo(
() => users.filter(u => u.active),
[users]
);🔵 useCallback — stable function reference
const onClick = useCallback(() => doStuff(id), [id]);Needed when passing handlers to memoized children — otherwise a new function ref each render defeats React.memo.
🧪 The profiler
React DevTools → Profiler tab → record an interaction → see which components rendered and why. Always profile before optimizing.
💻 Code Examples
memo + useCallback combo
const Row = React.memo(function Row({ user, onSelect }) {
console.log('Render', user.id);
return <li onClick={() => onSelect(user.id)}>{user.name}</li>;
});
function List({ users }) {
const [selected, setSelected] = useState(null);
// ✅ stable — Row.memo works
const onSelect = useCallback(id => setSelected(id), []);
return <ul>{users.map(u => <Row key={u.id} user={u} onSelect={onSelect} />)}</ul>;
}Rows only re-render when their user changes, not when selection changes.
useMemo for derived value
const sorted = useMemo(
() => [...items].sort((a, b) => a.price - b.price),
[items]
);Sort runs only when `items` reference changes.
⚠️ Common Mistakes
- Wrapping every component in React.memo — usually no benefit, sometimes slower (comparison cost > render cost).
- Using useMemo for trivially cheap calculations (e.g. addition) — pure overhead.
- Forgetting that React.memo only does SHALLOW prop comparison — new object literals defeat it.
- Skipping useCallback for a handler that's never passed to a memoized child — useless ceremony.
🎯 Interview Questions
Real questions asked at top product and service-based companies.
Q1.What does React.memo do?Intermediate
Q2.What's the difference between useMemo and useCallback?Intermediate
Q3.When does useCallback NOT help?Advanced
Q4.Why might memoizing cause a bug?Advanced
Q5.How would you find out which component is causing re-renders?Advanced
🧠 Quick Summary
- Profile FIRST — most apps don't need memoization.
- React.memo skips re-render if props are shallow-equal.
- useMemo caches values; useCallback caches functions.
- Memoization works only when references stay stable.
- Exhaustive-deps lint rule prevents stale closures.