useMemo & useCallback — caching values & functions

[check: …]
📖 guide (.md) ← react deep dive
📖 Pair this live React playground with the companion guide (.md) — this page is the rendered ground truth. ↗ Builds on frontend/react: useState (the re-render model these hooks optimize).

the problem: every render mints fresh references

When a parent re-renders, every inline function () {{}} and every object literal {{}} is a brand-new reference. A child wrapped in React.memo() shallow-compares its props, sees new references, and re-renders anyway — the memo did nothing. useMemo and useCallback keep references stable across renders so memoized children actually skip. useMemo caches a value; useCallback caches a function. Both recompute/recreate only when their deps change.

hooksignaturecachesre-runs when
useMemo useMemo(factory, deps) the result of factory() a value in deps changes (referential inequality)
useCallback useCallback(fn, deps) the function reference itself a value in deps changes — sugar for useMemo(() => fn, deps)
React.memo React.memo(Component) the last render output any prop fails a shallow Object.is compare — needs stable prop refs to help

1 · the memoization you write (edit me)

2 · Babel compiles JSX → element tree

<MemoDemo/> becomes React.createElement(MemoDemo). React.useMemo(fn, [count]) returns the cached fib unless count changed; React.useCallback(fn, []) returns the same function instance on every render. The gold-check proves the cache survives an unrelated re-render.

// (hit "compile & render" to see Babel's output)

3 · live React (memoization, proven)

Click count+1 and useMemo recomputes fib. Click other+1 and fib is still cached (the value did not recompute) — while the React.memo child updates only because its count prop changed, never because onClick wiggled. The gold-check drives this automatically.

live: · gold: fib(1)→(2)→(3), other+1 keeps fib(3)=2, child shows Clicked: 1

intent → pattern

intentpatternwhy
cache a costly value useMemo(() => compute(x), [x]) skips recomputation on unrelated re-renders
stable callback prop useCallback(handleClick, [dep]) keeps the fn ref identical so a React.memo child skips
stable object/array prop useMemo(() => ({'{'}a, b{'}'}), [a, b]) object literals are new every render — wrap them to keep refs stable
skip a child's re-render React.memo(Child) + stable props memo compares props shallowly; it needs BOTH value + ref stability to help
"just in case" memoization don't — measure with Profiler first deps comparison + cache storage cost more than the re-render you avoided