Indana
← Notes

Why Using One Object with

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 <code>useReducer</code>?

The Part That Looks Equivalent

At first glance, this approach seems to solve the same problem.

Instead of:

  • multiple <code>useState</code>
  • multiple setters

You now have:

  • one state object
  • one <code>setState</code>

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.