Why do React components re render?
A component re-renders when: (1) its state changes via setState, (2) its parent re-renders, (3) a context it consumes changes, (4) a hook it uses signals a change. Re-rendering is React's default — it does NOT mean a DOM update; React diffs the output and only commits actual changes. To skip a re-render, use React.memo with stable props, split contexts, or move state down.
Re-rendering is not a bug to be avoided — it's how React works. Understand the triggers first, then optimize.
The four triggers
- State changes —
setState(useState/useReducer) in this component. - Parent re-renders — by default, children re-render when the parent does.
- Context changes — any consumer of a Context Provider whose value changed.
- External store changes —
useSyncExternalStoresubscriptions (Redux, Zustand, etc.).
What re-rendering actually does
state change → React calls component function again → returns new VDOM
→ React diffs against previous VDOM → applies minimal DOM patchesA re-render is just the function running again. It costs CPU for the render, but DOM updates only happen for actual diffs.
Common surprises
Parent re-renders → all children re-render, even with same props.
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(c => c + 1)}>{count}</button>
<ExpensiveChild />
</>
);
}Fix: wrap ExpensiveChild in React.memo.
Inline objects/functions break memo.
<MemoizedChild onClick={() => doStuff()} style={{ color: 'red' }} />New function and new object each render — memo bails. Wrap in useCallback / useMemo.
Context update re-renders every consumer.
<UserContext.Provider value={{ user, setUser }}>The { user, setUser } is a new object each render. Every consumer re-renders. Split into separate contexts or memoize.
How to skip a re-render
React.memo(Component)— skip when props are referentially equal.- Stable references —
useCallback,useMemo. - Move state DOWN — keep state in the smallest subtree that uses it.
- Pull state UP into context only when many disparate components need it.
- Split a 'mega' state hook into smaller ones; co-locate.
How to skip a re-render — when NOT to
Most components are cheap. Memoization adds bookkeeping. Profile first; optimize hot paths only.
Mental model
The render is what produces the description of UI. The commit is what mutates the DOM.
You usually don't care about renders — you care about commits. React's diff algorithm makes most re-renders very cheap. Only when render cost is genuinely high (heavy calculations, large lists, complex JSX) does it pay to optimize.
Detecting unnecessary renders
React DevTools Profiler. 'Highlight updates when components render' shows what re-renders per interaction. Flame chart shows where time is actually spent.
Follow-up questions
- •What's the difference between a render and a commit?
- •Why doesn't React.memo prevent re-renders when an inline function is passed?
- •How does context cause cascading re-renders, and how do you fix it?
Common mistakes
- •Treating re-renders as inherently bad — they're cheap unless the render itself is expensive.
- •Adding React.memo everywhere — equality checks cost more than the saved render.
- •Putting a fresh object in a context Provider value, then wondering why everything re-renders.
Performance considerations
- •Render cost scales with the work in the function body + the size of returned JSX. Commit cost scales with actual DOM diffs. Most renders are sub-millisecond. Optimization targets are: huge lists, components doing heavy computation, frequent updates on big trees.
Edge cases
- •React.memo's default shallow equality compares by reference for objects/arrays — stable identities matter.
- •A component can re-render even if its state didn't change, if its parent re-rendered.
- •Context bypasses React.memo — consumers re-render on context value change regardless.
Real-world examples
- •Search-as-you-type re-renders the result list per keystroke — virtualization + memoization fix it. Drag-and-drop renders during every mousemove — moving state into a ref or external store avoids cascades.