React
Beginner
1 min read
Context with useReducer for Complex State
Example
import { createContext, useContext, useReducer } from 'react';
// ── Reducer ────────────────────────────────────────────────────────────────
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
return { ...state, items: [...state.items, action.item] };
case 'REMOVE_ITEM':
return { ...state, items: state.items.filter(i => i.id !== action.id) };
case 'CLEAR':
return { ...state, items: [] };
default:
throw new Error(`Unknown action: ${action.type}`);
}
}
// ── Two contexts: state + dispatch ─────────────────────────────────────────
const CartStateContext = createContext(null);
const CartDispatchContext = createContext(null);
export function CartProvider({ children }) {
const [state, dispatch] = useReducer(cartReducer, { items: [] });
return (
<CartStateContext.Provider value={state}>
<CartDispatchContext.Provider value={dispatch}>
{children}
</CartDispatchContext.Provider>
</CartStateContext.Provider>
);
}
export const useCartState = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);
// ── Consumer ───────────────────────────────────────────────────────────────
function CartBadge() {
const { items } = useCartState(); // re-renders on state change
return <span>{items.length}</span>;
}
function AddToCartButton({ product }) {
const dispatch = useCartDispatch(); // stable – never re-renders
return (
<button onClick={() => dispatch({ type: 'ADD_ITEM', item: product })}>
Add to cart
</button>
);
}