Why Action is a bad name for a Redux Action

Lucas Sunsi Abreu
magnetis backstage
Published in
11 min readApr 12, 2019

And a brief reflection on how the way we name things affects our cognitive understanding of the abstract concepts we deal with every day.

The empty look. The feeling of being misled. The need to reflect past actions.

First off, a bit of background. I consider myself a full stack developer and I've been working with single-page applications for some years now. I've started delving into React waters from the beginning of its weird alpha days. I learned all the hooks, got hyped, and went to happily fail at coding an application just complex enough to warrant the need for a state manager. Then it was Redux time: as it quickly settled the whole flux architecture debate, I learned all of its concepts, got hyped and went to happily fail at implementing proper state management to a shiny new project with it.

Fast forward to today, a few big projects later, and I'd say I've found a little less failure on the correct implementation of these technologies in my daily life. I also have been learning Elm in my spare time with the help and incentive of some amazing folks I work with every day (yeah, if you thought it might be you, it's probably you). Upon reflection, I do not think these two facts are unrelated, which lead me to think about the concepts I learned from the Elm side that improved my React/Redux workflow.

The first thing that came to mind was how much the understanding of what kind of information an Action should convey impacts the way you see and model your Store. It is a central piece of semantics to the whole architecture, and I don't think we give it enough importance when learning this state management architecture. At least we're not encouraged to.

Introduction got long so let's wrap it up. Maybe I'm the only one that has difficulty getting the Store modeling right and that's fine. This difficulty along with my experience in Elm led me to reflect on how the abstract concepts understanding makes a difference in my decisions about the technologies that currently affect my life. My focus in this post is the semantics I currently attribute to my Redux Actions and how I think the concept's name leads us away from the actual meaning they should have.

Prelude

I've always had a gripe with two patterns that feels very present on the JavaScript community to me: concepts, abstractions, and semantics are usually second to usability, practicality, and syntax; batteries are usually not included in our everyday packages, which leads the package owners not care about how their packages fit to the world.

Redux is a particular offender on these two aspects, in my opinion. I recently wrote an article on Redux's shortcomings, in which I paint a picture of how the absence of opinion and communication hurt the real world applications of an otherwise revolutionary library. The documentation is amazing at showing you how to use the library, but less than amazing on guiding you on how to see and think about your software.

One of the issues I point out in this previous article is exactly the vague semantics we have for Redux Actions. The more I coded Elm on my spare time and crossed its concepts into my real life Redux code, the clearer it was to me that Elm got it right. Further, I don't think this is a "they are different but each does their thing" situation, at least not on this subject. What I see is that who has more Elm experience has a better time modeling its Stores, while who goes directly to Redux has a more confusing time (which is, well, everyone).

So let's try to bridge this gap, shall we?

What is an Action?

We are all used to it, right? It's a plain object, that far we know. It has a required type key which tells us which kind of event and data it represents. Let's see what Redux has to say, coming from its documentation.

Actions are a payload of information that sends data from your application to your store. They are the only source of information for the store. You send them to the store using store.dispatch().

Pretty straightforward, I'd say. From this sentence, we get its semantics: It's the one and only form of communication between the application and your Store. You even already know how to dispatch them! I hope this bit of semantics is enough because it's all you get from this page.

So let's think together. I want to code a simple form that lets me change my email on some disruptive web platform. We need a text input and a submit button, with one action to go with each one. What should we name the actions? How about SET_EMAIL_TEXT and SUBMIT_EMAIL_FORM? Or even CHANGE_EMAIL_TEXT and SAVE_EMAIL? Or maybe REQUETS_EMAIL_CHANGE?

All these options work fine, I've written all of them before, and they all miss the point. The thing they have in common is the imperative mindset that our collective early education trained us to leverage, which is not a great fit with the architecture Redux proposes.

Communication, but for what kind of information?

We learned that actions were pieces of communication to our application, properly guided by Redux. This proposes that actions are a way of communication, which makes perfect sense.

The follow-up question, left as an exercise to the users, is: For what kind of information? What should my application be telling my store?

In my experience, there are two mainstream answers to this question. Most projects show a mix of the two: communication of intent and communication of an event. One is much more prevalent than the other, and I'll let you guess which one it is while we go through them in the following sections.

An Action communicates intent

Imperative verbs, for our imperative minds

The most obvious one would be that an action communicates an intent to the Store, which in turn will leverage the attached payload along with its internal state to carry it out. Following this mindset, most of your actions types will include a verb in the imperative form and your action log will read like a sequence of orders to the store.

This mindset is massively prevalent and it's absolutely no surprise. It fits very well to our imperative mindset and feels right from the programmer point of view: it makes sense for my store to recognize a CHANGE action if I'm trying to change something, it fits. Or does it?

The semantics for Actions we are left from this point of view is very close to the semantics we have for plain functions, right? It's a block of code that carries out some intent. Further yet, it leaves us feeling good because the actions allow us to think imperatively while React and Redux were pleading to favor declarativity. That's where things start to misalign.

If we see Actions and functions as close concepts, we start to share some traits that do not fit the Redux architecture. The most important and harmful one is that functions are made for reuse, while actions should be very context specific. Following this train of thought, we're inclined to try and think of our actions to be reusable, so maybe a REQUEST_EMAIL_CHANGE, which we already used on the password changing form, might come in handy when I need to change the user's email in the signup. I'm using it in two places instead of one!

The thing is, this is harmful to the architecture. In order to use the same action in two places, you need to have some logic built in. In my example, you'd have to wire the action to the click, and also to the user signup pipeline. This bit of logic will be outside the store, which is one indication that something is off. Further, it could be 'inside' the store, with a little help from one more middleware (maybe running a follow-up store cycle with a harmless loop, or even signaling a saga to do some work for you), but then you're just hiding the issue.

What I'm usually left with is a myriad of anti-patterns, ranging from logic outside the Store to one user action dispatching more than one action and passing through one action dispatching another with no real side effect between them. I'm just naming a few here, but I might write another article just brushing through these smells I've seen in a lot of Redux projects.

Well, we've been deep enough down the rabbit hole. I'm feeling kind of anxious on how relatable this is to me and I hope the same does not apply to you, but if it does you might be wondering: What would the proper communication be then?

An action communicates an event

Past tense verbs, let the Store figure out what to do

Yeah, I can imagine your face reading this since it's what we were talking about when we're jQuerying our way to success way before the hipsters came. The thing is, what was wrong with jQuery was not the events, but the imperative mindset. When we move to Redux and bring the mindset with us, we're just moving the problem around with more complex tooling while avoiding the real danger: the darkness within ourselves. So fake surprise and bear with me while I try to paint a picture of why this type of communication is better.

This semantics is a little further from the one from a plain function: it's a more specific view of them. Each reduction is a handler that knows what to do when something happens to the outside world. This mindset flips the responsibility arrow between the store and the application: the application should inform the Store of what's happening and the Store will know what to do, instead of the application informing the store what to do.

The difference may sound silly, but it really isn't: you wouldn't call an Action SET or REQUEST or CHANGE anymore, because these names are clearly conveying intention. The application should just let the Store know, so INPUT_CHANGED or BUTTON_CLICKED is more like it. Your Action log will look like a series of interactions, which is more like what the architecture is about. The whole point of having a Store is that it will be the heart and soul of your application, and will know how to deal with exactly the number of interactions the application supports.

That fact that USER_SIGNED_UP and CHANGE_EMAIL_FORM_SUBMITTED both saves the users email is just a business logic coincidence, which will for sure be abstracted to a function to avoid duplication, but it doesn't mean they should have a single action. You won't be inclined (or at least less so) to dispatch two Actions from a single interaction, because it does not make sense anymore. A single interaction (like a click, timeout or server response) is a single action because the action represents the whole interaction, not its intents. Finally, dispatching one Action from the reduction of another will seem weird, like this would mean the store is signaling events to itself, which violates the 'actions are a mean of communication with the outside world' premise.

Thinking of an action of a way to communicate events make you go from the imperative form verbs to the past tense verbs. The mindset change will better align your store modeling with the declarative model of thought, which in its turn will fit better with the React and Redux architecture.

What is wrong with the Action name then?

I proposed two ways to see and understand an Action being dispatched. Each way make us see its purpose differently, leading to different issues to solve on modeling our complex bleeding edge software. I clearly favored one over the other and now want to reflect a little bit about a little devil in our shoulder called cognitive ease.

We, as humans, have a lot of biases in our so-called rational thought. One of them is that we recurrently mistake things that are easy and recognizable as correct and adequate. It's a small misplacement of adjectives that help us filter the enormous influx of information coming from the world around us without much strain, but that makes things that strain our mind feel wrong. We also have a huge need for consistency coming from this information, so we connect every dot we can while discarding the ones that do not favor the easy picture we inevitably want to form while processing the outside.

Learning React and especially the Redux architecture is hard for most, mainly because they depart from the ever comfortable imperative view of the world. They ask you not to give orders, but to describe what you want from each world state. They urge you not to perform side effects and try to manage your complexity on a specific weird way, compared to what you are used to. In an environment like this, cognitive ease becomes an even stronger motor for the decisions we will make because nothing else feels right, and that's exactly where I think the Action name is nocive.

We end up sliding to the intention-informing order-giving action because it feels right. The mindset fits perfectly with imperative programming, which we cherish since our early days (and with good reason). It feels familiar because we're already in such a different place to what we're accustomed to, at least we can order our Store like we know worked for us in the past. And finally, it aligns perfectly with the 'Action' name, which suggests the communication should do something, should impose change, should act.

The thing is that we're led, at least in my opinion, to the wrong semantics, not just by the names chosen for the concepts, but by various reasons basing off our tendency to learn to code first with non-functional languages. This semantics drift leads to a worse (or at least more difficult) understanding of the amazing architecture proposed by Redux. The library and community make little effort to guide us through this conceptual understanding and always puts practicality first, which is a shame in my opinion because it pushes away many developers from other circles that would (and should!) be attracted by the concepts first and syntax later.

It’s not action, it’s a reaction. It’s not giving orders, it’s describing behavior. It’s not imperative, it’s declarative. And that’s why Action is a bad name for a Redux Action.

Epilogue

So what do I wanted from this post? I wanted to make my point about an aspect of a library very present in my daily life, but I also wanted to complain a little bit, if we're being honest. I think we as a community could do a better job of getting our concepts in order, defining and naming them better, and trying harder to teach them, even just between ourselves.

Not trying to answer all the problems is good and so is trusting your users, but there is a point where unopinionated just means leaving a problem to be solved by the community collective cognitive ease or even a single lonely developer trying to get around an architecture that is honestly too aggressive to newcomers.

I think Message is a better name than Action for the concept adopted by Redux, but that's not really the point. The point is that human understanding is fragile and can be swayed by the smallest details, so we might want to focus our understanding on the foundational concepts first, then how to apply them to our startup.

And hey, maybe I got it wrong. Maybe the semantics are different for a reason and I, not Redux, am missing the point here. But then you need to tell me, and please do!

--

--