Context + Reducer Pattern
RE · Patternssyntax
const StateCtx = createContext(null);
const DispatchCtx = createContext(null);
function Provider({ children }) {
const [state, dispatch] = useReducer(reducer, init);
return (
<StateCtx.Provider value={state}>
<DispatchCtx.Provider value={dispatch}>
{children}
</DispatchCtx.Provider>
</StateCtx.Provider>
);
}example
const TodoStateContext = createContext(null);
const TodoDispatchContext = createContext(null);
function todoReducer(state, action) {
switch (action.type) {
case 'ADD':
return [...state, { id: crypto.randomUUID(), text: action.text, done: false }];
case 'TOGGLE':
return state.map(t =>
t.id === action.id ? { ...t, done: !t.done } : t
);
case 'DELETE':
return state.filter(t => t.id !== action.id);
default:
throw new Error(`Unhandled: ${action.type}`);
}
}
function TodoProvider({ children }) {
const [todos, dispatch] = useReducer(todoReducer, []);
return (
<TodoStateContext.Provider value={todos}>
<TodoDispatchContext.Provider value={dispatch}>
{children}
</TodoDispatchContext.Provider>
</TodoStateContext.Provider>
);
}
function useTodos() {
return useContext(TodoStateContext);
}
function useTodoDispatch() {
return useContext(TodoDispatchContext);
}Note This pattern provides Redux-like state management with built-in React APIs. Splitting state and dispatch into separate contexts prevents components that only dispatch actions from re-rendering when state changes.