Context is a broadcast mechanism: when its value changes, every consumer re-renders. If one provider carries many unrelated values, a change to any of them re-renders components that only read the others. Splitting the context or memoizing the value isolates those updates.
Before you start
You should be comfortable with context, memo and re-render basics. This article focuses on update scope; it assumes correct provider placement.
Step-by-step walkthrough
Step 1: Give each consumer its own context
Put state and the updater in separate contexts: components that only dispatch do not re-render when the state changes, because they consume the dispatch context. This is the common useState-style split and often removes most unnecessary renders.
Step 2: Memoize the provider value
An inline object value={{ user, setUser }} is a new reference every render, so every consumer re-renders even when the data is equal. Wrap it in useMemo keyed on the actual values so the reference is stable.
Step 3: Reconsider the store for high-frequency updates
Context is a poor fit for values that change many times per second, such as a cursor position or a form draft. Use a subscription-based store, or keep high-frequency state local to the components that need it, so the broadcast does not fan out.
Worked scenario
State and dispatch live in separate contexts so a change re-renders only readers.
const StateContext = createContext(null);
const DispatchContext = createContext(null);
function Provider({ children }) {
const [state, dispatch] = useReducer(reducer, initial);
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
);
}Walk through the example
dispatch is stable across renders, so DispatchContext never changes and its consumers do not re-render when state updates. StateContext changes only when state changes, so only actual readers update. A button that only calls dispatch stays put while the list re-renders, which is the intended isolation.
Common mistake
Passing a fresh object as the provider value, which re-renders all consumers on every provider render even when nothing relevant changed. Another is putting unrelated data in one context, so a theme change re-renders form consumers.
Verify the behavior
Add a render counter to a dispatch-only consumer and confirm it does not re-render when state changes. Log renders before and after memoizing the value and compare the counts. Move a high-frequency value into context and observe the fan-out, then move it local and confirm the renders drop.
Interview exercise
Why split a context into state and dispatch contexts?
Answer and reasoning
Because consumers that only write (dispatch) should not re-render when the value changes. Since dispatch is stable, a separate dispatch context never updates, so button-like consumers stay mounted. Only the readers of the state context re-render, which reduces work without changing the API.
Continue learning
Compare placement in React context scope and memo reference stability. Read the React context performance guidance and try the React interview questions.