๐Ÿณ IntuitiveFE
Login
โ† All concepts

memo, useMemo & useCallback

โฑ๏ธ ~3-minute bite ยท solve the sandbox to master

0%lesson
๐Ÿง’

5-Year-Old Metaphor

โ€” The physical, real-world picture. No jargon.

๐Ÿ“„ React.memo = caching a printed document. If the original file hasn't changed, reprint is unnecessary. useMemo = caching a calculation. useCallback = caching a function reference so dependent caches aren't invalidated.

React.memo

Cache a printed document

Wraps a component. Skips re-render if props are shallowly equal to previous render.

Best for: Slow-to-render components that receive stable props

useMemo

Cache a calculation

Memoizes any value returned from a function. Re-computes only when deps change.

Best for: Expensive computations: filter/sort/transform of large arrays

useCallback

Cache a function reference

Memoizes a function. Prevents memo'd children from re-rendering when a callback prop changes.

Best for: Stable callbacks passed to React.memo'd children

The pairing rule

React.memo + useCallback are a pair. useCallback is only valuable if the function is passed to a React.memo component. Without memo, the child re-renders anyway โ€” useCallback provides no benefit, only overhead.

๐ŸŽ›๏ธ

Interactive Sandbox

โ€” Move something, see it react instantly.

Pattern

Child re-renders (simulated)12wasted renders

Parent re-renders โ†’ ALL children re-render, even if their props didn't change. Each parent state update cascades down the entire subtree.

Without Memo โ€” code
ts
1// Every time Parent re-renders, ChildA and ChildB re-render too
2function Parent() {
3 const [count, setCount] = useState(0);
4 return (
5 <div>
6 <button onClick={() => setCount(c => c + 1)}>+</button>
7 <ChildA value="static" /> {/* re-renders on every count change */}
8 <ChildB value="also static" /> {/* same */}
9 </div>
10 );
11}
12ย 
13function ChildA({ value }: { value: string }) {
14 // This logs on every parent click โ€” even though value never changes
15 console.log('ChildA rendering');
16 return <div>{value}</div>;
17}
Gotcha: Most re-renders are fast and not a real problem. Profile first with React DevTools Profiler before adding memo. Premature memoization adds complexity without measurable gain.
Insight: React's default behavior is correct for most cases. The reconciler is fast โ€” the virtual DOM diff is cheaper than a complex memoization layer. Only memo components that are genuinely slow to render.
Explored:๐Ÿ”โญ๏ธ๐Ÿงฎ๐Ÿ”—๐Ÿšซ
๐ŸŽฏ

Challenge

Explore all 5 patterns. Understand when memo helps and when it hurts.

Try it
๐ŸŽฏ

Why Should I Care?

โ€” The exact interview question + the bug it kills.

Interview questions

Q: What is shallow comparison and why does it matter for memo?

js
1// Shallow comparison: compares by reference, not deep value
2// Primitives: compared by value (OK)
3prevProps.count === 42 โœ“
4ย 
5// Objects: compared by reference (tricky)
6prevProps.user === nextProps.user // true only if SAME object
7ย 
8// Bug: parent creates new object each render
9function Parent() {
10 // { id: 1 } creates a NEW object reference every render
11 return <MemoChild config={{ id: 1 }} />; // memo never skips!
12}
13ย 
14// Fix: stable reference with useMemo
15const config = useMemo(() => ({ id: 1 }), []);
16return <MemoChild config={config} />;

Q: When does React.memo hurt performance?

When the comparison is more expensive than the render. A React.memo'd component that receives frequently-changing props runs both the comparison AND the render on every update โ€” strictly worse than no memo. Profile before adding memo.

Q: Why is `() => doSomething()` passed as prop problematic?

js
1// Arrow function in JSX = new reference on every render
2<MemoChild onSave={() => save(formData)} />
3// โ†’ MemoChild.memo comparison: prevProps.onSave !== nextProps.onSave
4// โ†’ always true โ†’ memo never skips โ†’ waste of React.memo
5ย 
6// Fix: useCallback stabilizes the reference
7const handleSave = useCallback(() => save(formData), [formData]);
8<MemoChild onSave={handleSave} />
๐Ÿ”ฌ

The Deep Dive

โ€” Spec refs, engine internals, the minutiae.

React Compiler โ€” automatic memoization

React 19's Compiler analyzes component code at build time and automatically adds React.memo, useMemo, and useCallback where they are provably safe. Components that currently need manual memoization will be handled automatically. The compiler is opt-in via a Babel/SWC plugin and requires components to follow React's rules (no mutation, pure renders).

React DevTools Profiler โ€” how to find slow renders

js
1// 1. Open React DevTools โ†’ Profiler tab
2// 2. Click Record, interact with your app, click Stop
3// 3. Flamegraph shows each component's render time
4// 4. "Ranked" chart shows slowest components first
5ย 
6// Target: any component >16ms in the critical path
7// is a candidate for memoization
8ย 
9// 5. Check "Why did this render?" (DevTools setting)
10// โ†’ shows which prop changed to trigger the re-render
11// โ†’ if it was a new function/object: stabilize with useCallback/useMemo

Structural sharing in state updates

When you update a deeply nested object, Immer (used by Redux Toolkit) creates a new root reference but preserves unchanged subtrees by reference. This means React.memo'd components that receive unchanged subtrees correctly skip re-rendering โ€” Immer's structural sharing makes shallow comparison effective even for deep data.

๐ŸŽค

Interview Questions

โ€” Real questions from real interviews โ€” with answers.

memo caches a component; useMemo caches a value; useCallback caches a function reference.

Arrow functions in JSX create a new reference on every render, making shallow prop comparison always fail.

When props change frequently โ€” memo adds comparison cost on top of render cost, making it strictly worse.

Wrap the computation in useMemo with the raw data and filter/sort params as deps.

The compiler automates memoization โ€” manual memo/useMemo/useCallback become largely unnecessary.

Immer preserves unchanged subtree references, so memo'd children receiving unmodified subtrees correctly skip re-rendering.

๐ŸŽฎ

Memory Game

โ€” Quick quiz โ€” lock the concept in long-term memory.
1/4

You add React.memo to a component and profile again โ€” render time is unchanged. The most likely cause is: