Rendering Performance & Memoization
The problem
As a component tree grows, re-rendering components whose output hasn't actually changed wastes work — memoization skips that wasted work by reusing a previous result when the inputs are provably the same, but applying it incorrectly can silently break correctness or provide no benefit at all.
Why now
The virtual DOM and reconciliation already established that re-rendering (the function running again) is distinct from and cheaper than a real DOM update, but it's still not free at scale. Memoization is a direct, practical application of the pure-function idea from higher-order-functions: if a function's output depends only on its inputs, and the inputs haven't changed, you can safely skip calling it again.
Mental model
Memoization is a cache with exactly one entry: remember the last inputs and output, and if the new inputs are equal to the last ones, return the cached output instead of recomputing. This only works safely if the wrapped function is pure (same input always gives same output) — which is exactly why memoizing a component with side effects or unstable references (a new object literal every render) either fails silently or does nothing.
Requires
- The Virtual DOM & ReconciliationReact
Memoization's entire value proposition is skipping re-renders that reconciliation would otherwise have to diff; you need to already understand what a re-render costs before optimizing it away is worth reasoning about.
- Higher-Order FunctionsProgramming Fundamentals
React.memo and useMemo are literally higher-order functions — functions that take a function or value and return a cached/wrapped version — applying that pattern directly to the rendering problem.
Used in
- React.memo for skipping a component's re-render when its props haven't changed
- useMemo and useCallback for stabilizing values and function references across renders
- React DevTools' Profiler, used to identify which components are actually worth memoizing
- Common performance anti-patterns — memoizing everything by default, which can add overhead without benefit
Projects
- Use the Profiler to identify a component that re-renders unnecessarily on every parent update, then wrap it in React.memo and confirm the re-render disappears
- Break memoization by passing an inline object or function as a prop, observe that React.memo provides no benefit, then fix it with useMemo/useCallback
Examples
- React.memo(ExpensiveList) skips re-rendering ExpensiveList entirely if its props are shallow-equal to the previous render's
- Passing onClick={() => doThing()} inline creates a new function reference every render, defeating a child's React.memo unless wrapped in useCallback
Resources
Mastery checklist
- I can explain what React.memo does and the equality check it relies on
- I can identify why an inline object or function prop defeats memoization
- I can use the Profiler to find a component actually worth memoizing, rather than guessing
- I can explain why over-applying memoization can add overhead without meaningful benefit