Choosing between useState and useReducer is one of the most common decisions React developers face. Both hooks manage state in functional components, but they excel in different scenarios. This guide will help you understand when to use each one.
The Basic Difference
useState is the simpler option - you set an initial value and get back the current state plus a function to update it:
const [count, setCount] = useState(0);
setCount(count + 1);
useReducer is more sophisticated - it takes a reducer function and initial state, returning the state plus a dispatch function:
const [state, dispatch] = useReducer(reducer, initialState);
dispatch({ type: 'INCREMENT' });
When to Use useState
Use useState when:
- Managing simple values - strings, numbers, booleans
- Independent state pieces - when state updates don't depend on each other
- Simple updates - straightforward state transitions
- Prototyping - quick and easy to set up
// Perfect useState use case
const [name, setName] = useState('');
const [age, setAge] = useState(0);
const [isActive, setIsActive] = useState(false);
When to Use useReducer
Use useReducer when:
- Complex state objects - multiple related values (e.g., { name: '', age: 0, address: {} })
- Dependent state updates - when new state depends on the previous state in complex ways
- Many actions - when you have multiple ways to update state
- Logic reuse - when you want to extract and reuse state logic
- Testing - reducers are pure functions, easier to test
// Perfect useReducer use case - form with dependent fields
const formReducer = (state, action) => {
switch (action.type) {
case 'SET_FIELD':
return { ...state, [action.field]: action.value };
case 'RESET':
return initialState;
case 'SUBMIT_START':
return { ...state, isSubmitting: true, errors: null };
case 'SUBMIT_SUCCESS':
return { ...state, isSubmitting: false, submitted: true };
case 'SUBMIT_ERROR':
return { ...state, isSubmitting: false, errors: action.errors };
default:
return state;
}
};
Key Decision Points
| Scenario | Recommendation |
|---|---|
| Single boolean toggle | useState |
| Form with 5+ fields | useReducer |
| Loading/error states | useReducer |
| Simple counter | useState |
| Undo/redo functionality | useReducer |
| Dropdown selection | useState |
| Complex wizard/stepper | useReducer |
The React 19 Perspective
In React 19, the game changed significantly with new hooks:
- useActionState - simplifies form state management with built-in pending and error states
- useOptimistic - handles optimistic updates for better UX
- use - new primitive for handling promises and context
These new hooks often replace what developers previously used useReducer for, making state management even simpler in React 19.
Performance Considerations
Don't choose between them purely for performance. Both hooks have similar performance characteristics. The real performance gains come from:
- Using
React.memoon child components - Using
useCallbackfor event handlers passed to children - Using
useMemofor expensive calculations
Conclusion
Start with useState for simplicity. When your state logic becomes complex, or when you find yourself updating multiple pieces of state that depend on each other, switch to useReducer. The rule of thumb: if you're spreading objects like setState({ ...state, newValue: x }), consider using useReducer instead.
Pro Tip: You can always refactor from useState to useReducer later. Don't over-engineer - start simple and optimize when needed.