Why Using One Object with useState Isnât the Same as useReducer
Using a single object with useState can organize your data, but it doesnât organize how that data changes. useReducer goes further by centralizing all state transitions in one place, making complex logic easier to understand and maintain.
At some point, I started questioning useReducer.
If the goal is to manage multiple related states, couldnât I just do this?
const [state, setState] = useState({
step: 1,
form: { name: "", email: "", password: "" },
errors: {},
isSubmitting: false,
isSuccess: false
})Everything is already grouped.
No scattered state. No multiple hooks.
So the question becomes:
Why would I still need useReducer?The Part That Looks Equivalent
At first glance, this approach seems to solve the same problem.
Instead of:
- multiple
useState - multiple setters
You now have:
- one state object
- one
setState
Cleaner structure. Less repetition.
It feels like:
problem solved
Where It Starts to Feel Off
The difference doesnât show up in the shape of the state.
It shows up in the logic.
Updates start to look like this:
setState(prev => ({
...prev,
isSubmitting: false,
isSuccess: true
}))And elsewhere:
setState(prev => ({
...prev,
isSubmitting: false,
errors: { submit: "Failed" }
}))Individually, these are fine.
But over time:
- similar logic gets repeated
- related fields are updated in multiple places
- it becomes easier to forget something
The Missing Structure
The real issue isnât how the data is stored.
Itâs how the data changes.
With this approach, every update is:
âmanually construct the next stateâ
Thereâs no shared definition of:
- what actions exist
- how state should transition
What useReducer Changes
useReducer introduces a constraint:
all state changes must go through a single function
Instead of writing updates everywhere:
dispatch({ type: "SUBMIT_SUCCESS" })
dispatch({ type: "SUBMIT_ERROR", payload: error })And handling them in one place:
case "SUBMIT_SUCCESS":
return {
...state,
isSubmitting: false,
isSuccess: true
}The Real Difference
Itâs not about how many states you have.
Itâs about where the logic lives.
With one-object useState
- state is centralized
- logic is scattered
With useReducer
- state is centralized
- logic is also centralized
Why This Matters
As the app grows:
- more actions appear
- more conditions are added
- more fields are updated together
Without structure, updates become:
harder to track, easier to break
With a reducer, you get:
- explicit transitions
- predictable behavior
- a single place to understand the system
The Shift in Thinking
At first, it feels like:
âI just need to organize my stateâ
But the real problem is:
âI need to organize how state changesâ
Final Takeaway
Using one object with useState solves the shape of your data.
But it doesnât solve the shape of your logic.
And thatâs the gap useReducer fills.
Not by adding more complexityâ
but by forcing your state transitions to live in one place, where theyâre easier to see, reason about, and maintain.