React Curriculum

useMemo & useCallback: Referential Stability

Medium100% Free~25 mins#react#usememo#usecallback#memoization#referential-equality#react-memo
Interview Answer (30-Second Summary)

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 cycles
2function simulateMemo() {
3 const logs: string[] = [];
4
5 // Render 1
6 logs.push('parent-render');
7
8 // Callback wrapped in useCallback preserves reference identity
9 const fn1 = () => 'action';
10 const fn2 = fn1; // useCallback with stable deps returns same pointer
11 const isCallbackStable = Object.is(fn1, fn2);
12
13 if (isCallbackStable) {
14 logs.push('child-memo');
15 }
16
17 return `${logs.join(' ')} ${isCallbackStable}`;
18}
19
20console.log(simulateMemo());
Predict Console & Render Output
A

parent-render child-memo true

B

parent-render false

C

child-memo parent-render true

D

parent-render child-render false

Execution Trace
Step 1 of 3 (Line 6)

Parent component re-renders.

Component / Call Stack
simulateMemo()
Hook State & Scope
logs[0]:"parent-render"
Console Output(No console logs emitted)
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:

js
{} !== {} // true!
(() => {}) !== (() => {}) // true!

The Misconception: "Optimization Everywhere"

Many candidates believe wrapping every function in useCallback makes an app faster. In reality:

  • Calling useCallback creates an inline function, allocates dependency array memory, and runs Object.is comparisons on every render!
  • For ordinary buttons (<button onClick={handleClick}>), memoization is pure overhead.

When Memoization Is Actually Required

  1. Passing to React.memo Children: If a child component is wrapped in React.memo, passing a new inline function breaks shallow prop equality, forcing the child to re-render.
  2. Hook Dependency Arrays: If a callback or object is listed in the dependency array of useEffect, useCallback prevents the effect from running on every render.
  3. Expensive Pure Computations: If a function loops through 10,000 items with heavy math, useMemo caches 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
What is the difference between useMemo and useCallback?

useMemo caches the returned value of a function, whereas useCallback caches the function definition itself so its reference remains stable.

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.

Yes. useCallback(fn, deps) is functionally equivalent to useMemo(() => fn, deps).

No. It only preserves the identity of the callback function across renders of that component.

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.