It didn’t start with Redux.
It started with a tiny piece of state and a feeling that everything was under control.
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.
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.
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.
store.dispatch({ type: "SET_USER", payload: userData });And reading it anywhere felt… powerful.
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.
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.
dispatch({ type: "USER_LOGGED_IN", payload: userData });I leaned more on selectors instead of directly reaching into state.
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.