Back to writings

When My State Management Turned Into a Maze

Feb 24, 2026 · Author: Edwin

It didn’t start with Redux.

It started with a tiny piece of state and a feeling that everything was under control.

JAVASCRIPT
const [isOpen, setIsOpen] = useState(false);

A button clicked, a modal opened. That was the whole story. Simple, clean, no overthinking.

But things never stay that simple for long.

It Started Growing… Quietly

Soon, I needed to store user data. Then loading state. Then a bit more logic.

JAVASCRIPT
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(false);

At that point, it still felt manageable. Everything was in one place, and I didn’t have to think too hard about it.

Until the same user was needed somewhere else.

So I lifted the state up. Passed it down. Threaded it through components that didn’t even care about it.

That’s when I thought, okay, this is getting messy… maybe Redux will fix this.

Redux Felt Like Relief

The first time I moved things into Redux, it felt like I had finally “done it right.”

One store. One place to look.

JAVASCRIPT
const initialState = {
  user: null
};

function reducer(state = initialState, action) {
  switch (action.type) {
    case "SET_USER":
      return { ...state, user: action.payload };
    default:
      return state;
  }
}

Updating state felt clean.

JAVASCRIPT
store.dispatch({ type: "SET_USER", payload: userData });

And reading it anywhere felt… powerful.

JAVASCRIPT
const user = useSelector((state) => state.user);

No more passing props through five layers. No more guessing where data lived.

For a while, it actually felt like I had solved the problem.

Then Things Started Feeling… Off

Nothing broke. That’s what made it tricky.

But slowly, things became harder to follow.

A component needed a slightly different version of user. Another one depended on a nested field. Somewhere in the code, I reached for getState() just to quickly grab something.

JAVASCRIPT
const currentUser = store.getState().user;

It worked instantly. No friction.

And that’s exactly why I kept doing it.

Over time, the store stopped feeling like a clean source of truth. It started feeling like a shared space where everything depended on everything.

When It Became a Maze

Debugging changed.

It wasn’t about fixing obvious bugs anymore. It was more like… tracing paths.

Why did this component re-render? Why is this value correct here but wrong there? Why does changing one thing affect something completely unrelated?

I wasn’t lost in broken code. I was lost in connections.

That’s when it hit me — I hadn’t built a bad system.

I had built a maze.

Finding My Way Out

The solution wasn’t to throw Redux away. It was to slow down and be more intentional.

Not everything needed to live in the store. Some state was perfectly fine staying local.

I stopped using generic actions and started naming things more clearly.

JAVASCRIPT
dispatch({ type: "USER_LOGGED_IN", payload: userData });

I leaned more on selectors instead of directly reaching into state.

JAVASCRIPT
const userName = useSelector((state) => state.user?.name);

And I became a bit more careful with getState(), using it only when it actually made sense.

Nothing magical happened overnight. But gradually, things started to feel understandable again.

What Stayed With Me

The tricky thing about state management is that it doesn’t fail loudly. It just becomes harder to think.

Redux didn’t create the problem. It just gave me the power to spread state everywhere without realizing the cost.

And somewhere along the way, I learned this the hard way:

State isn’t just data you store. It’s something your entire app starts to depend on.

If you don’t stay intentional with it, you won’t notice when it turns into a maze…

Until you’re already inside it.