When to Define Action Creators in Redux

By Dave Ceddia Comment

Redux is often criticized for requiring a lot of boilerplate code to make anything happen. One of the primary offenders is the action creator – a function that exists solely to return a plain object. They often seem like excessive abstraction for the sake of abstraction.

Abstraction

This post will go over why action creators exist, why they’re worth using, and when you can skip them.

Why Write Action Creators At All?

It’s a fair question. In Redux, actions are plain objects, like this one:

{
  type: USER_LOGGED_IN
}

An action usually represents some kind of event – like the beginning of an API call, or a user logging in.

Because it can be error-prone and annoying to type out these action objects by hand whenever you need one (or, let’s be honest – copy and paste them), it’s common to hand off the creation to an action creator, like this one:

function userLoggedIn() {
  return {
    type: USER_LOGGED_IN
  };
}

An action creator is a plain function that returns an action object.

Right about here is where the complaint of “boilerplate” comes up. A whole function, just to return a simple object? Do you really need an action creator for each and every tiny action?

Well, no. You don’t really.

If an action is extremely simple – just a type and nothing else – it might not be worth making an action creator for it. If you feel burdened by the boilerplate, don’t write the boilerplate. It’s your code, after all :)

When Are Action Creators Useful?

The example above was a very simple action. As simple as they come.

Maybe your action is more complicated, though. Maybe it looks something like this:

{
  type: USER_LOGGED_IN,
  payload: {
    username: "Somebody",
    email: "somebody@somewhere.com",
    eventDate: "2017-02-09T02:29:15.952Z"
  }
}

This action still represents a user logging in, but it’s more complex than before. It carries a payload of data related to the action.

It’s up to you whether you put the data at the top level, or inside a payload key, or even follow the Flux Standard Action guidelines. The format doesn’t matter so much.

This is what’s important: when you copy-and-paste an action in multiple places, change is harder.

If the backend devs decide that loginDate is a better name than eventDate, you’ve gotta track down every occurrence and update it.

Or, if keeping data under the payload key is causing you anguish, you have to find every usage and change its structure.

This problem is what action creators exist to solve. Simply offload the creation to an action creator function:

function userLoggedIn(username, email, loginDate)
{
  type: USER_LOGGED_IN,
  payload: {
    username, email, loginDate
  };
}

And now, any time you need to dispatch a USER_LOGGED_IN action, you just call this function. Want to refactor the action’s structure later? Easy – it’s only in one place. Want to change its type to USER_LOGIN_EVENT or something? Go for it.

Mix and Match? Or Consistency Throughout?

The choice to use action creators exists on a spectrum.

Maintainability vs. Boilerplate

Consistent & Maintainable

You can create an action creator for every single action – even the tiny ones. This will give you the best maintainability, because everything is abstracted into a function. And, if you have a bit of an OCD streak like I do, you’ll enjoy the consistency from having everything using the same pattern.

Least Boilerplate

On the other hand, you can eschew action creators entirely. For simple apps, or those with simple actions, this is a perfectly reasonable option. Sometimes they’re just not worth the effort.

If your app is something you’re going to eventually throw away, or it needs to be done yesterday (“refactor it later!”), skipping action creators could be a good option.

Or, hell, if you just hate them. No shame. You can always refactor if you change your mind.

Mix and Match

There’s a middle ground, if you don’t mind some inconsistency. You can make action creators for the complex actions (anything with more than a type), and skip them for the simple actions. Or decide case-by-case: if an action will only ever be used in one place, maybe just don’t make an action creator for that one.

Fourth Option: Outsource It

There is a popular library called redux-actions which reduces the code required to make an action creator. It turns simple actions from this:

function userLoggedIn() {
  return {
    type: USER_LOGGED_IN
  };
}

Into this:

import { createAction } from 'redux-actions';

const userLoggedIn = createAction('USER_LOGGED_IN');

The API for redux-actions is simple and terse. Its docs are straightforward and full of examples. Brush up on your ES6 knowledge before reading, though. If you’re looking for maintainability, consistency, and less boilerplate, check out redux-actions.

Wrap Up

We went over the pros and cons of action creators – how they make code easier to refactor at the expense of a bit more boilerplate.

We also looked at some options for reducing the boilerplate, and how you don’t have to choose between “all action creators all the time” and “no action creators ever.” Mix and match as you see fit. It’s your code.

If you found this helpful, sign up to my newsletter below. I write articles like this every week or so.

comments powered by Disqus