Redux has gaps and pitfalls

Lucas Sunsi Abreu
magnetis backstage
Published in
9 min readOct 16, 2018

We're falling on them for our own good, but for how long?

She is really careful not to fall, but it makes her really nervous.

Redux took us by storm when the whole Flux Architecture discussion was shining brightly. I remember there being many implementations of the general architecture with many distinct characteristics, varying wildly both in the interpretation of the structure itself and in the opinion of how far the reach of the state manager should be. What questions does this abstraction need to answer?

A predictable state manager for JavaScript apps

Then we get Redux. It had a few fundamental premises coming directly from functional programming. It had a dead simple implementation which anyone could grasp, along with a dead simple API anyone could understand in a glance. By itself, it answered very few of the opened questions, but it was flexible enough so that the community could answer the missing ones by themselves.

Instantly all other Flux implementations seemed convoluted and complicated. The purity of the reducer truly made software more predictable, especially those time traveling toy projects used to sell that the concept was worth it. Quickly came the integration with the ever-rising rendering library React and it clicked perfectly with the functional side of it, while also answering the concern about the view knowing too much of anything.

Everyone was Redux at this point or at least a Redux-inspired state management framework made for your other-than-React rendering library. The community immediately got to answer those unanswered questions. Want to do anything? There's a middleware for that. Don't agree with how the middleware does it? There will be a middleware for that, which you just created a repository to and already have ninety stars to show off.

But at what cost?

Here we are today, and what's this article about? I'm learning Elm in my free time, along with reading up about GraphQL and Apollo. I've also worked with Redux on a few projects and use it daily on my regular job on a couple of projects.

Looking outside in, I see these indecisions left a dent on the image and reality of the library's usage and will probably be why we move away from it when it comes to it. This article is not about criticizing, it's about pointing out these gaps, how we can answer them, and how we having to answer them impacts our lives and families (ok, got a little carried away there).

I think it's important to point out that these are just my opinions on the subject, and as all of my opinions on any subject, I fully expect them to be wrong. The only variable between my agreement and disagreement with these words is time. Getting this out of the way, let's get started.

Action semantics are vague

In Redux, actions are payloads sent from your application to your store. They are the only way you can change the application state and therefore the only way you can make your view update. They are represented as a primitive object with a special property telling which kind of action it is.

This sounds perfect, a simple concept with strong implications. An action informs the Store of something so it can change its state accordingly. So if you have two actions being reduced with similar or overlapping functionality, how do you keep your code dry and extract the similar processing so it can be shared?

This is a very common question that has two fundamentally different answers that end up telling on what semantic your actions have and what you understand by "application". You could think that the shared functionality could be extracted to a separate action, or you could think the separate functionality could be extracted to a utility function.

The first approach considers an action a block of processing, overlapping with the concept of a generic function. The second one separates what a function is from an action represents, bringing the semantics of actions closer to messages.

I've seen both approaches in practice and keep seeing this confusion being made. I'm in the opinion that if you see actions as just functions, you have a misconception on the semantics.

I might be right or wrong about that, but maybe the dual interpretation could be avoided by a stronger definition on this fundamental part of the abstraction being proposed if anything to lower the number of decisions on it the user has to take.

combineReducers is misleading

You got the whole reducer concept: there can be only one and all your logic goes into it (hopefully). Inevitably you'll want to split up this logic in many small blocks, making testing easier and better organizing your project. If you are really diligent, you're probably going for a ducks approach and using the included utility tool that comes with redux to combining all your reducers.

The thing is, splitting your logic into blocks isn't that simple. One of the cool things about redux is that the reducer is just a function, and you can do whatever you want to split its logic. You could go for helpers, for various reducers with access to whatever piece of state, you name it.

If you are really sure some piece of logic is completely isolated though, you could go with isolating it completely from the rest of the ordeal, using combineReducers. One thing to note is that this is one of the most restrictive cases possible. It is the exception, not the rule.

If you go crazy dividing every bit of logic you have and encapsulating every piece of data into a reducer, you'll end up questioning how to communicate the reducers. You'll end up moving data outside your store just to get it in through another reducer and you might even use some global state fetching to avoid paying the communication cost.

Of course, this is about bad modeling and experienced developers will have better luck avoiding all of it, but the open-ended approach of redux along with the most restrictive helper as a default comes off as a little misleading to me, at least for newcomers.

Data reading gets messy

One state rules the whole application. There is only one truth, and it's the truth of the store. This concept is the center of redux and is one of the single most amazing concepts we got in front-end development.

Thing is, you have one object and that’s it. Even for a huge application with tons of actions, reducers, logic, and love, you get just one object. It's amazing, but also pretty scary. The object tends to get huge, with various branches and conditions only known through the depths of the reducer.

So when you finally get to read it and show it on the page, you have to be careful. What is nullable and when? Can these two variables be set at the same time? Where do I write the pre-processing I need on this array before sending it to the view? Should the view do it?

Redux does not have an answer. It's your logic, you deal with it. Just don't go duplicating business logic on the view in order to display the right popup at the right time. Fortunately, this problem is wonderfully tackled by some amazing libraries like reselect, which both adds to the awesome as well as the entry barrier of the whole thing.

Side effects are an afterthought

Functional programming is cool and all, but we all know that side effects are literally what makes your application do anything. You need side-effects, but your reducer has to be side-effect free. There's gotta be an opinionated way to side-effect without breaking architecture, right?

Redux by itself doesn't answer this. It doesn't say where it's cool to side-effect, it just says where it isn't. So we have to assume everywhere else is fine, right? So we start launching requests from component hooks like the old times, or maybe we move them to the action creator? But then I need the action creation to be asynchronous, how can I do that?

I think this might be redux's biggest gap. Side-effects are what makes your startup go and you need them, but when you starting learning redux, you're flooded with wildly different takes on how they fit in your application. You don't even know if you like redux yet and you have to choose from thunks to sagas, from cycles to loops.

More to the point, thunks are one of the most popular side-effect middleware, but they don't even try to model the problem. They were coded as a side-effect middleware proof-of-concept, but the community was in such desperate need of an official solution, we quickly elected thunks to be it, even though it isn't a solution at all.

Routing integration is… not?

If by any chance you're looking into redux to manage the state of your single page application, chances are you are going with something like react-router to take care of the client-side routing.

The library is actually pretty great. It's tidy and the documentation is good. At last, we are getting some tempo in the React/Redux world, eh? You have a single source of truth so you figure the route state should be kept on it.

Then you see you are discouraged on doing that. Redux offers no official solution on this and the default router for React encourages you on breaking the single source of truth premise. You get confused and decide to push through on the integration, only to realize why you were being discouraged in the first place.

Of course router integration is beyond redux's problem and it would be unfair to put the lacking integration layer as it's responsibility. That said, routing is a crucial part of a single page application and redux users are bound to get some level of frustration on the subject.

Maybe Redux is bad?

We got a little ranting and pointing session above, but it's over now. You might be thinking, if redux is lacking all of this, maybe it's a bad way to go? Maybe my energy should go elsewhere, somewhere more… mature and sane?

Yeah, things look a little crazy, but this isn't on redux. The JavaScript community has got to be one huge behemoth of passion, and all it cares is the future. There's no other programming community in the world so ready and eager to change, to experiment, to preprocess, to add and combine, and ultimately, to figure what works.

Redux isn't bad, in fact, it's amazing. It took guts to propose a paradigm shift from what we were used to in state management. It took even more so to avoid answering questions that weren't ready to be answered yet.

One thing we can learn from all the deceased Flux implementations is that a bad answer is worse than no answer at all, at least in this community. We want to be able to experiment and explore and figure things out. We will do it through middlewares and through enhancer, and even through higher-order middlenhancer (patent pending) if necessary.

Redux laid down the foundation and capitalized on the amazing community to build what we have today.

Where do we go from here?

Of course, there's no single answer to that clickbaity subtitle. I mean, who are you?

Are you learning redux for the first time and were looking whether it was worth it or not? It is worth it, get on board and learn something new while contributing back.

Are you looking for confirmation on your doubts on what ground the library should cover but doesn't? Get in here, give me a hug. We're in this together, it's frustratingly awesome that it gives us an opportunity to think for ourselves.

Do you disagree with everything I said and did I just miss the entire point of JavaScript? You're awesome and you're probably right! Drop me a text so I can see that as well and have something to do after work.

Are you just reading this because you are my friend and get nothing of what's happening but will clap for it anyway? Hey, this article was longer than usual, you didn't need to put yourself through it. Our friendship is tight, take care of yourself first. Love you though!

Are you Redux?

If you are, I have to thank you first of all. You broke my imperative mind and got me hooked on functional thought. More so, you gave me an opportunity to practice, talk and learn about foreign concepts and share them with my friends, even at regular work hours.

I'm absolutely sure these gaps and pitfalls allowed and encouraged awesome experimentation. How many people actually understand and use sagas on their front-end like it's nothing, how many people learned about memoization and what a damn thunk is. It's truly amazing.

But maybe Redux is still too raw. Maybe what it encouraged in experimentation is also what makes it hard to learn. Maybe the micro-libraries make so that the people that don't know they exist get stuck in solved problems. Maybe it's misuse on account of vague abstractions clouds its image and the progress made by all this research.

Maybe we need to make some hard choices in order to grow even further.

--

--