To split React context by responsibility, create separate contexts for the value and its update function. A component that only dispatches actions then re-renders only when the dispatch function changes, not when the value changes.
The one-big-context problem
A context value often bundles data with the function that changes it, like a task list with a dispatch function. Every component that reads the bundle re-renders when the list changes, even a button that only adds tasks.
Separate the value from the updates
Create two contexts instead of one. The first holds the value, and the second holds the function that updates it.
import { createContext } from "react";
export const TasksContext = createContext(null);
export const TasksDispatchContext = createContext(null);The value context holds the current task list. The dispatch context holds the function that sends actions, which stays the same object across renders.
Provide both from one component
Wrap the tree in both providers from a single TasksProvider component. The component owns the state and passes the value and dispatch separately.
import { useReducer } from "react";
export function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
return (
<TasksContext value={tasks}>
<TasksDispatchContext value={dispatch}>
{children}
</TasksDispatchContext>
</TasksContext>
);
}The state still lives in TasksProvider. Its value and its updater reach the tree through two independent channels.
Add a custom hook for each side
Export hooks that read each context. The hooks hide the two contexts and give components a clear name for what they need.
import { useContext } from "react";
export function useTasks() {
return useContext(TasksContext);
}
export function useTasksDispatch() {
return useContext(TasksDispatchContext);
}A component that renders the list calls useTasks. A component that only adds tasks calls useTasksDispatch, so it re-renders only if the dispatch function changes, which it normally does not.
Where the split pays off
The benefit appears when some readers only update and some only display. A task input that dispatches an action stops re-rendering when the task list changes, because it never subscribed to the value context. The list still re-renders, because it reads the value context directly.
What splitting does not solve
Components that all read the same value context still re-render together when that value changes. To reduce that, you need memoized values or smaller pieces of state, which the re-render article covers. See why context causes re-renders and how to reduce them.
When one context is still fine
Do not split a context just to split it. If every reader needs both the value and the update function, one context is simpler and perfectly clear. The split pays off when a meaningful group of readers only dispatches and would otherwise re-render for nothing.
What to learn next
For many providers in one tree, the same split scales cleanly when you combine multiple context providers.
Rune AI
Key Insights
- Create one context for the value and one for the update function.
- Provide both from a single provider component.
- Export a custom hook for each context.
- Readers that only dispatch skip value re-renders.
- The dispatch function rarely changes identity.
Frequently Asked Questions
Why split a context into two?
What are the two contexts typically called?
Does splitting remove all re-renders?
Conclusion
Split a context by responsibility by creating separate contexts for the value and its update function, then expose each through a custom hook. Components that only dispatch actions stop re-rendering when the value changes, and the wiring stays in one provider.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.