React Context: Sub-Tree Re-Render Cost and Selector Memoization
- Last updated
- Prerequisites:
- React Context API
- React.memo and useMemo
- react
- context
- memoization
- re-render
- performance
- useContext
Read at your depth
The practical view
When a Context Provider's `value` changes (by reference equality `Object.is`), **every component calling `useContext(MyContext)` will re-render**, even if the component only cares about an untouched field inside the context object. Wrapping consumers in `React.memo()` does **not** stop context-triggered re-renders. To optimize, split large contexts into smaller, specialized contexts or memoize sub-components.
The same idea in other frameworks
Splitting State / Dispatch contexts or using `use-context-selector`.Angular hierarchical injectors with fine-grained Signals.Vue `provide` / `inject` where reactivity is granular via individual `ref` pointers.Svelte `setContext` / `getContext` holding independent reactive `$state` objects.Legacy vs modern
Monolithic Context (Over-rendering) vs Split Context Architecture
Monolithic context objects trigger re-renders on unrelated consumers; splitting state and dispatch prevents wasteful rendering.
<UserContext.Provider value={{ user, setUser, theme, setTheme }}> // ❌ Renders all consumers on any change<ThemeContext.Provider value={theme}><UserContext.Provider value={user}> // ✅ NEWInterview gotchas
Context
This is one of the most common React performance interview traps.
The mechanical answer
No. `React.memo` only prevents re-renders when a component's **props** are unchanged. If a component calls `useContext(MyContext)` and the Provider's value changes, React will **always** re-render that component, completely bypassing `React.memo`.
Trap
To prevent child subtrees from re-rendering, you must extract the context-consuming hook into a parent component and pass the extracted value down to a memoized child component via props.
Context
Tests knowledge of reference equality in the Context propagation algorithm.
The mechanical answer
If you pass an inline object literal `<Provider value={{ user, settings }}>`, a brand new object memory reference is created on *every single parent render*. Because React uses `Object.is` to check for context changes, all consumers will re-render on every parent render regardless of whether the inner data changed. Wrapping the value in `useMemo` ensures the object reference remains stable.
Trap
Omitting `useMemo` on Provider object values makes the entire consumer tree re-render on every parent update.