Lesson 8 of 80
React.memo, useMemo, and useCallback all skip work by comparing references — which means a fresh reference every render silently defeats all three.
15 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 4
What's the correct fix for the React.memo problem above?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Three tools, one idea: skip work if nothing actually changed.
React.memo(Component) skips re-rendering a component if its props are shallowly equal to last time.useMemo(fn, deps) skips recomputing a value if deps haven't changed.useCallback(fn, deps) skips creating a new function if deps haven't changed.All three compare by reference, not by deep equality. That single fact is where almost every memoization bug comes from.
const Child = React.memo(function Child({ onClick }) {
/* ... */
});
function Parent() {
const handleClick = () => doThing(); // ❌ new function every render
return <Child onClick={handleClick} />;
}
Child is memoized, but Parent hands it a brand-new function every single render. React.memo's shallow comparison sees a different onClick reference every time — as far as it can tell, the prop changed — so Child re-renders anyway. The memoization did nothing.
Parent re-renders. Without useCallback, handleClick is a brand-new function reference every single time.
useMemo/useCallback have their own small overhead (storing and comparing the dependency array) — reach for them when the computation is genuinely expensive or the reference stability itself is what matters (like feeding React.memo), not everywhere by default.useState lesson — useMemo/useCallback don't protect you from that; they're built on the same closure mechanics.React.memo's default comparison is shallow, same as Object.is on each prop. Passing an object or array prop still needs that object to keep the same reference across renders — memoizing the component doesn't memoize its props for you.useMemo is not a lifecycle guarantee. React is allowed to discard a memoized value and recompute it later (e.g. to free memory) — don't rely on useMemo for anything with side effects; that's what useEffect is for."Why doesn't React.memo prevent this re-render?" is a favorite because the bug looks like it shouldn't exist — the component is memoized — and explaining it requires connecting three ideas from earlier lessons at once: reference equality (from props), closures (from useState), and reconciliation's shallow comparison.