While both hooks manage state, the choice between them usually comes down to the complexity of the logic and the structure of the data.
🤝 Side-by-Side Comparison
Feature | useState | useReducer |
|---|---|---|
State Type | Best for primitives (string, number, boolean). | Best for objects, arrays, or nested data. |
Update Logic | Simple: | Complex: Logic is centralized in a reducer function. |
Transitions | Independent updates. | Multiple sub-values that change together. |
Business Logic | Mixed within the component JSX. | Decoupled (can be moved to a separate file). |
Testing | Harder to test state transitions alone. | Easy to test the reducer as a "pure function." |
✅ When to use useState
Use useState for simple, independent pieces of state where the next state doesn't really care about the previous state's history.
Toggle switches (on/off).
Input field values.
Basic counters.
Simple data fetching where you just store the result.
✅ When to use useReducer
Use useReducer when you have "state transitions"—where an action (like "checkout") triggers a series of complex changes across the state object.
Complex Forms: Validating multiple fields at once.
Shopping Carts: Adding/removing items and calculating totals simultaneously.
Game Logic: Managing scores, health, and levels based on various player actions.
Global State: When passing state down through Context,
dispatchis often easier to manage than multiple "set" functions.
👍 The "Rule of Thumb"
If you find yourself writing setSomething 3 or 4 times in a single function to update related data, or if your useState logic is making your component file too long to read, it’s time to switch to useReducer.