Back to React
React
medium
mid

How would you isolate which React component is re rendering unnecessarily and fix it?

Measure first with the React DevTools Profiler and the 'highlight updates' / why-did-you-render tooling. Identify the cause (unstable props, context, parent re-render), then fix with memoization, state colocation, splitting context, or stabilizing references — not by sprinkling React.memo everywhere.

6 min read·~12 min to think through

Unnecessary re-renders are a measure-then-fix problem. Guessing leads to over-memoization, which adds its own cost and complexity.

Step 1 — Isolate it (measure)

  • React DevTools Profiler — record an interaction, look at the flame graph. It shows which components rendered, how long they took, and why each rendered ("props changed", "hooks changed", "parent rendered").
  • "Highlight updates when components render" (DevTools settings) — visually flashes re-rendering components. A component flashing on every keystroke that shouldn't = your suspect.
  • why-did-you-render library — logs exactly which prop/state reference changed and whether it was deep-equal (i.e. a "fake" change).

Step 2 — Diagnose the cause

Common culprits:

  1. Parent re-renders → all children re-render (unless memoized).
  2. Unstable prop references — a new object/array/function literal created in render each time, breaking React.memo.
  3. Context — every consumer re-renders when any part of the context value changes.
  4. State too high — state lives in a parent, so a change re-renders a big subtree that didn't need it.
  5. New children element identity each render.

Step 3 — Fix the actual cause

  • Colocate state — move state down to the component that uses it. The cheapest fix; often eliminates the problem entirely.
  • useMemo / useCallback — stabilize object/array/function props passed to memoized children. Only meaningful if the child is actually memoized.
  • React.memo — memoize a component whose props rarely change but whose parent re-renders often.
  • Split context — separate fast-changing and slow-changing values into different providers; or pass setters via a stable context separate from the value.
  • Lift content via children — a parent that re-renders for its own state doesn't re-render children passed in from above.
  • Selector-based stores (Zustand/Redux) — subscribe to slices, not the whole store.

The discipline

Don't memoize preemptively. React.memo and useMemo have a cost (comparison + memory) and clutter the code. Memoize where the profiler shows a real, repeated, expensive re-render. React Compiler (when adopted) auto-memoizes and changes this calculus.

Follow-up questions

  • Why can React.memo still re-render even when you wrap a component in it?
  • When is useMemo/useCallback actually doing nothing useful?
  • How does the children prop pattern avoid re-renders?
  • How does React Compiler change the memoization story?

Common mistakes

  • Sprinkling React.memo/useMemo everywhere without measuring.
  • Memoizing a component while still passing it a fresh object/function prop each render.
  • Putting fast-changing values in a wide context.
  • Keeping state high in the tree when it could be colocated.

Performance considerations

  • Memoization isn't free — it costs a comparison and memory. The win has to outweigh that. Profile to confirm a re-render is both frequent and expensive before optimizing. Colocation is usually cheaper and cleaner than memoization.

Edge cases

  • A memoized component with children — children identity changes each render.
  • Re-renders that are cheap and harmless (don't optimize those).
  • Lists where every row re-renders because the row component isn't memoized.
  • Inline-defined components recreated every render.

Real-world examples

  • A search input that re-rendered an entire results grid on each keystroke — fixed by colocating the input state.
  • A theme context bundling theme + a frequently-updated value, split into two providers.

Senior engineer discussion

Seniors lead with measurement (Profiler, why-did-you-render) and treat memoization as a targeted fix, not a default. They name the structural fixes first — colocation, children-as-props, context splitting — because those remove the problem rather than masking it, and they mention React Compiler shifting manual memoization toward obsolescence.

Related questions