When I first got into React, state management was a very important architectural concept. Redux was, for all intents and purposes, used on every major project. I suspect its ubiquity today has to do with that. At the time it was a major tool in React: the idea of a global state, something you can access from anywhere without worrying about scope, prop drilling, etc.
We’ve learned a lot of lessons since then. One of the most exciting things about developing with React is you can observe, almost in real time, how our perspective on the problem domain of UI development evolves. And once hooks were introduced, and it shook up the way we interacted with React code all together.
Redux’s place in the React ecosystem was questioned at the advent of hooks. A lot of different state management libraries came around with seeming great APIs. Zustand had a simpler approach without the requirement of reducers, which made it more intuitive for a lot of apps. Recoil, similarly, went with an atomized approach to state which seemed more modular and sensible. Yet Redux stuck around, now rebranded as RTK.
A year or two later, React (now Tanstack) Query became popularized. It provided a declarative approach to interfacing with APIs, which felt greatly improved over what was, in application code, calling services as part of a run-on-init useEffect. But perhaps more importantly, Tanstack Query included an API cache. Developers began to realize that most of their ‘state’ wasn’t state at all, but rather API data. And still, Redux stuck around: now under the guise of RTK Query, a “Reduxed” version of a declarative API library and feature parity with React Query.
I think in modern React, Redux, and most other state managers need to go the way of the dinosaur. Especially for normal ‘business CRUD’ applications. It’s not just that they’re not needed, it’s that describing your application in terms of ‘global state’ is often a harmful mental model, bad for modularizing, encapsulating, and keeping concerns separate.
I have written some fairly complex React / React Native code, from using React state to manage the overworld UI and inventory of an HTML based game, to complex financial and healthcare products that required massive redundancy. And the number of times I’ve actually reaped the benefits of a complex, multi-step state management pattern is exactly zero.
And I’m not even recommending React context as a replacement for Redux. Just… don’t have global state, use an API cache, and use props. The way React was meant to be used. Props are extremely powerful not least because they provide a natural interface to separate concerns, instead of adding a dimension of coupling to a global state manager.
useState
. Seriously, see how far you can go with just state. Maybe a reducer and context to handle a highly localized feature.“But I don’t want to cause rerenders. My app needs to be performant.”
I doubt it. Most of the time people talk about “reducing rerenders” very flippantly. I think React devs sometimes see it as an arbitrary standard of performance optimization they should strive towards. Certainly, you don’t want needless rerenders. But in my experience, performance is something hyper contextual to which there is no silver bullet. It is trivial to memo
things to avoid setting off rerender chains in high level parents, which I find far more valuable for performance than anything a global state manager pretends to afford. Most “rerender issues” have to do either with lists or complex UI DOMs, neither of which Redux “solves”.
If you’re a strong believer in Redux, or any other state manager for that matter, I urge you to try to manage client state without a library and just use an API cache. I think you’ll find that Redux won’t be missed.