- React
- Advanced Hooks, Referential Equality, and Stores
- useMemo & useCallback: Referential Stability
useMemo & useCallback: Referential Stability
useMemo caches the result of an expensive calculation, while useCallback caches the function instance itself. Their primary interview value is preserving referential equality for props passed to memoized children (React.memo) or hook dependency arrays.
Key Learning Objectives
Contrast useMemo (caches return value) with useCallback (caches function identity).
Explain why passing new inline arrow functions or object literals breaks React.memo.
Analyze the memory and CPU cost of memoization versus raw re-computation.
Identify when the React Compiler eliminates the need for manual memoization.
The Interview Problem & Context
What is logged to the console when a parent component re-renders while passing a memoized callback and a non-memoized object to a React.memo child?
1// Simulating referential stability across render cycles2function simulateMemo() {3 const logs: string[] = [];45 // Render 16 logs.push('parent-render');78 // Callback wrapped in useCallback preserves reference identity9 const fn1 = () => 'action';10 const fn2 = fn1; // useCallback with stable deps returns same pointer11 const isCallbackStable = Object.is(fn1, fn2);1213 if (isCallbackStable) {14 logs.push('child-memo');15 }1617 return `${logs.join(' ')} ${isCallbackStable}`;18}1920console.log(simulateMemo());
Predict Console & Render Output
parent-render child-memo true
parent-render false
child-memo parent-render true
parent-render child-render false
Execution Trace
Step 1 of 3 (Line 6)Parent component re-renders.
Deep Architectural Explanation
The True Purpose of useMemo and useCallback
In JavaScript, creating a function or object literal produces a brand new memory reference on every single execution:
{} !== {} // true!
(() => {}) !== (() => {}) // true!The Misconception: "Optimization Everywhere"
Many candidates believe wrapping every function in useCallback makes an app faster. In reality:
- Calling
useCallbackcreates an inline function, allocates dependency array memory, and runsObject.iscomparisons on every render! - For ordinary buttons (
<button onClick={handleClick}>), memoization is pure overhead.
When Memoization Is Actually Required
- Passing to
React.memoChildren: If a child component is wrapped inReact.memo, passing a new inline function breaks shallow prop equality, forcing the child to re-render. - Hook Dependency Arrays: If a callback or object is listed in the dependency array of
useEffect,useCallbackprevents the effect from running on every render. - Expensive Pure Computations: If a function loops through 10,000 items with heavy math,
useMemocaches the result until inputs change.
Common Interview Traps & Pitfalls
Wrapping every callback in useCallback even when passed to regular DOM elements (<button>, <input>).
Forgetting that useCallback(fn, deps) is just syntax sugar for useMemo(() => fn, deps).
Missing dependencies in the dependency array, causing stale closure bugs.
Senior Interviewer Follow-Up Probes
How does the new React Compiler (React Forget) automate memoization and make manual useMemo redundant in modern apps?
Why does useCallback not stop the parent component from re-rendering itself?
How do you measure whether a useMemo calculation is genuinely worth the memoization overhead?
Frequently Asked Interview Questions
Why does passing an inline callback break React.memo?
Because a new function instance is created on each render. React.memo uses shallow equality (Object.is) to compare props, sees a new reference, and re-renders the child.
Is useCallback equivalent to useMemo?
Yes. useCallback(fn, deps) is functionally equivalent to useMemo(() => fn, deps).
Does useCallback prevent the component it is declared in from re-rendering?
No. It only preserves the identity of the callback function across renders of that component.
How does the React Compiler affect useMemo and useCallback usage?
The React Compiler automatically inserts memoization at the compiler level for values and callbacks, drastically reducing the need to write manual useMemo and useCallback in future codebases.
