What is useReducer? useReducer
is a React Hook that provides an alternative to useState
. It's particularly useful for managing complex state logic that involves multiple sub-values or when the next state depends on the previous one.
Basic Syntax: const [state, dispatch] = useReducer(reducer, initialState);
// Reducer function signature
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
// Usage
dispatch({ type: 'increment' });
dispatch({ type: 'decrement' });
Counter with useReducer View Code Count: 0
Actions performed: 1
+1 -1 Set to 10 Reset
✅ This example shows how useReducer manages complex state with action history tracking
Form Handling with useReducer View Code ✅ This example shows complex form state management with validation using useReducer
useState vs useReducer View Code useState ✅ Simple state updates ✅ Independent state variables ✅ Less boilerplate code ✅ Good for basic state management ❌ Complex state logic becomes messy ❌ Multiple related state updates useReducer ✅ Complex state logic ✅ Related state updates ✅ Predictable state transitions ✅ Better for testing ❌ More boilerplate code ❌ Overkill for simple state When to use useReducer: • State has multiple sub-values • Next state depends on previous state • Complex state logic • State transitions need to be predictable • Multiple components need same state logic • State updates involve multiple actions
Best Practices & Common Gotchas View Code ✅ Best Practices • Always return new state objects (don't mutate) • Use TypeScript for type safety • Keep reducer functions pure (no side effects) • Use switch statements for action types • Throw errors for unknown actions • Use action creators for complex payloads ❌ Common Gotchas • Mutating state directly instead of returning new object • Forgetting to handle all action types • Making reducer functions impure (side effects) • Using useReducer for simple state (overkill) • Not using TypeScript (loses type safety) • Putting too much logic in components instead of reducer 💡 Pro Tips • Combine with useContext for global state management • Use useCallback for dispatch functions passed to children • Test reducer functions separately from components • Consider using immer for complex state updates • Create action creators for better maintainability • Use lazy initialization for expensive initial state