apocalypse

dev

Redux is not dead (yet)

#...and probably never will be

If you're anything of a frontend enthusiast, you've probably put your hands on React Query, Zustand or React Server Components already. I guess you could call them the new standard when it comes to data fetching and state management in React. They are smart, concise, at least somewhat elegant and generally nice to work with.

Whenever Redux is mentioned, you may think "phew, who uses that anymore?", but that very question will reveal that you never worked on a big commercial React project, cause, let me tell you, it's all Redux out there. I mean, maybe not all, but like, still a whole lot.

To prove I'm not hallucinating, like your favorite AI chatbot may, here's the chart straight outta npmtrends. If you want to see it for yourself - here's the most recent version.

current-trends-updated

Where's your God now, tech bros?

Obviously, those statistics may be a little skewed and inflated, because the amount of weekly downloads doesn't equal the number of projects that have used this library in a given week, but if this chart shows one thing, it's the premise of this article - Redux is not dead. Whether you like it or not.

While we're at it, we could make a few observations off of this chart:

  1. Each technology is getting an upward trend - more and more people adapt those technologies in their projects. That includes Redux as well.
  2. There are quite a lot of non-React projects that use Redux.
  3. Redux Toolkit isn't as widely adopted as it should. We'll talk about it later.

#Why still use it?

Alright, so if there are at least a couple of decent alternatives to Redux when it comes to state management and data fetching, why not migrate to them and let Redux die peacefully? Well, that's the problematic part. Redux is not just a library, it shapes the architecture of your application. Can you call it a framework, then? Well, I'm not here to argue about definitions, but I think in a way it is one.

It is just really painful to even consider changing your entire app architecture from Redux-based to a non-Redux-based. The potential pain of such migration grows proportionally to the size of the project you work on. Not to mention the costs of this process and risks that it will induce. That's why so many big, commercial projects, especially the ones that started around 2018, when Redux was the shit, are still utilizing Redux instead of switching to something else, perhaps better.

I think it's as simple as that. I remember back in the days that Redux was recommended for larger applications, despite its rather complex approach, strict set of rules, unnecessarily large boilerplate and equally unnecessarily troublesome configuration. The alternatives sadly weren't that much better. It was the pre-hooks era, when even the Context API was a huge pain in the A, because in order to extract the value from it, you had to use Consumer's render props and the code became atrocious.

#Why do we actually hate it?

During the time of writing this post, the results of State of Frontend 2024 became public. I read through them sort of independently of this post, and the frequency illusion hit me right as I scrolled down to the state management libraries section.

state-of-frontend-2024

It's impressive for a technology, to be disliked by roughly everyone who tried it! Redux Toolkit (which is still Redux, but with smaller boilerplate) got slightly smaller dislike rate, but it's still over 50%, so I wouldn't take that as a win. Zustand, on the contrary, was reportedly used by similar amount of people and the dislike rate is incomparably smaller.

I think that in the previous chapter I already mentioned at least a couple of reasons why we hate Redux so much, but let's dig a little deeper. I also think it may be a little late to tell this, but Redux isn't inherently bad or wrong. There are situations where it's still the best tool for the job. The thing is - there aren't that many such situations.

Let's quickly run through some of the biggest offenses towards Redux.

#Too much boilerplate

The most prominent issue that people (including me) have with Redux, is the sheer amount of code you have to write in order to perform the most basic operations. While Redux wasn't really designed for the most basic operations, it was still unnecessary complex in its implementation, which was proven with the introduction of Redux Toolkit, that cut the boilerplate amount in half, maintaining the functionality.

Regardless of whether you're using classic Redux or Redux Toolkit, you still have to follow the Redux paradigm, in which there are actions, payloads, states, selectors, reducers, dispatchers, middlewares, thunks/sagas, immutability and the rest of the gang.

Let's look at the typical Redux data flow, pictured in the official docs:

redux-data-flow

In order to trigger a state change, you must:

  1. (optional, but rarely not used) Have an action creator that would return desired action upon calling. Action is just an object with type: string and payload: unknown properties
  2. Pass this action to dispatcher
  3. Have a registered reducer that would catch this action by its type, perform some computations based on current state, action type and often its payload, and return new state, that under no circumstances can be mutated

To use this state, you must:

  1. Have a selector that would, well, select this particular piece of state from an entire state tree
  2. Register this selector in the component with useSelector (or mapStateToProps if you're vintage)
  3. Expect new data triggering component re-render only after the reducer returns new data. If it happens more often - you're not memoizing enough. If it doesn't happen when it should - you're memoizing too much.

At some point you begin to ask a question - "why does it have to be so complicated?" The thing is, it doesn't. That's the reason why so many people despise Redux and move away from it - there are much better, simpler and cleaner approaches to the problem that Redux was designed to solve.

But wait, there's more! More reasons to dislike Redux, I mean.

#Complicated configuration

Another big issue. Setting up Redux, even with no add-ons, was problematic already and required quite a lot of boilerplate to do it. When you wanted to add some additional middlewares, enable devtools, or, God forbid, integrate SSR, the word that comes to my mind to describe such config is "crazy".

If you don't have the war flashbacks yet, I send you to the official documentation of both classic Redux and Redux Toolkit, to see for yourself how an example of Redux config looks like.

Not that bad? Now let's look at another example of some real production configuration with a classic Redux, that uses sagas, devtools, is SSR-ready and all that jazz:

store.js
import { combineReducers, createStore, applyMiddleware, compose, } from 'redux'; import createSagaMiddleware from 'redux-saga'; import logger from 'redux-logger'; import { reducer as formReducer } from 'redux-form'; import rootReducer from './reducers'; import rootSaga from './sagas'; export const sagaMiddleware = createSagaMiddleware(); export const configureStore = (preloadedState = {}) => { const combinedReducers = combineReducers({ ...rootReducer, form: formReducer, }); const middleware = [sagaMiddleware]; // Add logger middleware only in development mode if (process.env.NODE_ENV === 'development') { middleware.push(logger); } const composeEnhancers = ( typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ) || compose; const store = createStore( combinedReducers, preloadedState, composeEnhancers(applyMiddleware(...middleware)) ); sagaMiddleware.run(rootSaga); return store; }; export const store = configureStore(window.__PRELOADED_STATE__ || {});

Crazy enough? It is to me. Also keep in mind that if you use TypeScript and want to maintain strict type safety, you're up for even bumpier ride. Even if you use Redux Toolkit, you will still end up with a pretty uncanny config. It is hard to read, hard to modify, hard to maintain. Hard to like, basically.

#No batteries for a reasonable SSR solution

My last offense towards Redux is related to the fact, that it was very often used as a data layer for the application - Redux orchestrated data fetching and mutations with middlewares such as thunks or sagas. And okay, nothing wrong about that, yet. But now it's time for a little quiz.

Alright, it may not be a very tough quiz. If you don't want to cry over poor web vitals score and pages bloated with loaders, you have to use some form of server side rendering. So, since Redux was often used for data fetching, it probably should have some ready-to-use solutions for SSR, right? Or at least some convinient way of calling particular actions on the server, right? Wrong.

If you wanted to render your Redux-powered application on the server, you had to write the solution yourself. Well, mostly. There was this documentation page on how to server-render the app with Redux, but it basically showed you how you can do it yourself from scratch.

Couple that with the complicated config and complicated integration with react-router, and voilà. You've ended up with an unmanagable mess. Good luck maintaining that for another 10 years, until the last person that knew how this spaghetti works left the company and now the rewrite seems inevitable.

I know, I may exaggerate the outcome. But really, even for a library-not-framework, missing ready-to-use utils for SSR made many, many projects' architecture convoluted, way too complicated to maintain and simply understand by an average developer, who just wants to get the job done.

It is also another reason, why it may feel impossible to ditch Redux in so many projects. When the entire architecture is built around it, from scratch, both on the client and the server side, it really isn't achievable to switch to something different just like that. Choosing to live with a known evil is the only option going forward, which brings me towards the end of this article.

#What if I have no choice but to continue using Redux?

First off, I feel you. Been there, done that. Wasn't the worst thing to do, but definetely not the best either. There are a few things you should consider, if you're in that situation.

Make sure you're using Redux Toolkit. While it doesn't help much with the complexity of an architecture, it certainly reduces the amount of code that you have to write, and that's often good enough. You can migrate from classic Redux to Redux Toolkit relatively smoothly: start with the configuration, then update reducers one at a time until you've fully transitioned away from classic Redux. Modern Redux isn't all that bad - it has RTK Query, Immer built-in, better TypeScript support, among a few other perks. Read the docs and see for yourself!

Make sure you're following all the good practices, understand how Redux work, how to optimize selectors (and reduce redundant re-renders as a consequence), how to use immer effectively. If you can't escape Redux, make sure it doesn't kill the performance of your application.

Lastly - Touch Some Grass™ ;). Not enjoying Redux code is a First World problem - however uncomfortable, it won't kill you.

#The new jQuery

I could easily name this article "Redux is the new jQuery". We will probably never fully escape from Redux, and it will forever remain a part of the frontend landscape, just like the latter. Its extensive use across so many professional and commercial projects This isn't necessarily a bad thing on its own. I would consider Redux a step in the wrong direction, as we didn't know any better back then. Now, the best we can do is learn a few lessons from it:

  • truely global state is usually unnecessary
  • beware libs that require too much boilerplate for the simplest things
  • if it feels too complicated, it probably will stunt your fun
  • even though something is popular, it doesn't mean it's the right tool for you
  • data fetching should mostly happen on the server; look for tools and solutions that make this easy out of the box
  • even the hottest technology can become deprecated fairly quickly

If you arrived to the end of this article, thank you for reading and I wish you a lot of Redux-free apps to work on!

Related tags:

react
redux
react-redux
reduxjs
toolkit