Mastering React Re-renders: When, Why, and How to Optimize Performance
This comprehensive guide explains what React re‑renders are, distinguishes necessary from unnecessary re‑renders, outlines the four triggers—state, parent re‑render, context, and hooks—and provides practical patterns and anti‑patterns, including memoization, useMemo/useCallback, and key management, to improve component performance.
This article is a translation of the “React re‑renders guide: everything, all at once”. Click “Read original” to view the source.
1. What is re-render
When discussing React performance, we focus on two rendering phases:
initial render : the component renders for the first time on the page.
re‑render : an already mounted component renders again, triggered by data updates, user interactions, async requests, or external subscriptions.
If a page never updates data, it will never re‑render, so performance optimization is unnecessary.
Necessary re‑render : occurs when data changes and the component must display the latest state, e.g., typing in an input updates state on each keystroke.
Unnecessary re‑render : caused by poor implementation that forces the whole page to re‑render on each input change, leading to wasted work and possible UI lag.
Although React handles many unnecessary re‑renders efficiently, excessive or heavy re‑renders can cause noticeable jank.
2. When does a React component re‑render
Four main reasons trigger a re‑render: state changes, parent (or child) re‑render, context changes, and hook changes. A common misconception is that prop changes always cause a re‑render; this is not true unless the component is memoized.
re‑render reason: state change
When a component’s state changes—typically inside callbacks or
useEffect—the component re‑renders.
re‑render reason: parent component re‑render
If a parent re‑renders, its children also re‑render. The reverse is not true: a child’s re‑render does not trigger the parent.
re‑render reason: context change
When a Context Provider’s value changes, all consuming components re‑render, even if they don’t use the changed part. Memoization cannot fully prevent this, but workarounds exist (see section 7).
re‑render reason: hooks change
All hook activity belongs to the component that uses it. State and context updates inside hooks trigger re‑renders, and nested hooks follow the same rules.
State changes inside hooks trigger a re‑render.
If a hook uses context and the context value changes, the component re‑renders.
Re‑render myth: prop changes
For components not wrapped in
React.memo, prop changes alone do not cause a re‑render; the parent’s re‑render is the driver. Only memoized components re‑render when their props change.
3. Avoiding re‑render: component composition
Anti‑pattern: creating components inside render
Instantiating a component inside the render function forces React to destroy and recreate it on every re‑render, leading to flicker, state reset, lost focus, and missed effects.
State lifting to child components
Move state that only affects a small part of the UI into a smaller child component, preventing the large parent from re‑rendering when that state changes (e.g., toggling a dialog).
Children as props
Encapsulate heavy components as children props; because children are just props, they won’t re‑render unless the encapsulating state changes.
Component as props
Wrap heavy components in a smaller wrapper and pass them as props; the heavy component won’t re‑render because its props are stable.
4. Avoiding re‑render: using React.memo
Wrapping a component with
React.memoprevents re‑rendering unless its props change, which is useful for pure UI components.
React.memo with props
Non‑primitive props should be memoized before being passed to a memoized component.
React.memo with children or component props
Wrapping only the parent is ineffective because children and props are objects that change on each render, causing child re‑renders.
5. Using useMemo / useCallback to improve re‑render performance
Anti‑pattern: useless useMemo / useCallback props
Wrapping child props in memoization does not prevent re‑render if the parent re‑renders.
Useful useMemo / useCallback
If a child is memoized, all its non‑primitive props should also be memoized.
Hooks that depend on non‑primitive values (e.g.,
useEffect,
useMemo,
useCallback) should receive memoized inputs.
Using useMemo for expensive calculations
useMemo can avoid costly calculations on every render, but it has its own overhead and should be used for expensive React element creation rather than simple array operations.
6. Improving list re‑render performance
The
keyvalue influences list rendering. Setting a stable string key (e.g., an item’s id) is essential; using random numbers or indexes in dynamic lists leads to poor performance and state bugs.
Anti‑pattern: random numbers as keys
Random keys cause elements to be remounted on each render, degrading performance and breaking controlled inputs.
7. Avoiding Context‑induced re‑render
Memoize Provider values
If a Provider isn’t at the root, ancestor changes can re‑render it; memoizing its value prevents unnecessary updates.
Split data and API in Context
Separate data and functions into different Providers so components that only use the API don’t re‑render when data changes.
Split data into smaller Providers
When a Context holds multiple independent data blocks, break them into nested Providers so only the affected parts re‑render.
Context selector pattern
For components that only need part of a Context, use higher‑order components or memoization tricks to emulate a selector and avoid full re‑renders.
KooFE Frontend Team
Follow the latest frontend updates
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.