A Simple Naming Convention for Action Creators in Redux.js

Redux

Structuring your Redux application correctly the first time can be difficult -- especially if you don't fully understand the role actions play in the system.

The official Redux.js documentation uses examples like addTodo() and requestPosts().

What's wrong with the examples?

Adding a Todo

This snippet from the Redux documentation is the action creator for adding a todo to a list.

let nextTodoId = 0export const addTodo = (text) => {  return {    type: 'ADD_TODO',    id: nextTodoId++,    text  }}

In the context of the TodoMVC example application, the ADD_TODO action type and the addTodo() action creator are adequately named because the action tells a reducer to add a todo. We'll see later, however, how this naming doesn't work when adding a todo is an asynchronous operation.

Request Posts

This snippet from the Redux documentation is the action creator for requesting posts from a subreddit.

function requestPosts(subreddit) {  return {    type: REQUEST_POSTS,    subreddit  }}

The REQUEST_POSTS action type and requestPosts() action creator are poorly named because the action is not telling a reducer to request posts. Take a look at the associated reducer:

function posts(state /*...*/, action) {  switch (action.type) {    /* ... */    case REQUEST_POSTS:      return Object.assign({}, state, {        isFetching: true,        didInvalidate: false      })    /* ... */  }}

The action is simply telling the reducer that something is happening (i.e. we're fetching something and that it hasn't been invalidated yet). This naming might, at best, confuse a newcomer and, at worst, mislead someone to put side effects in their reducer (e.g. perform an API call from inside the reducer).

A Simple Naming Convention

The English language has two similar words, affect and effect.

(for a full explanation, see here: Affect vs. Effect )

Naming Action Types

When dealing with asynchronous web applications, your actions should generally describe effects. That is, your action should describe a change that has happened in your system. For example, a user logged on, some data was loaded, an API call was started, an error was encountered, etc.

You then dispatch the action to your store, whose job it is to reconcile the change with the entire application state.

Avoid REQUEST_POSTS and RECEIVE_POSTS because they suggest they affect the system. Going with REQUESTING_POSTS and RECEIVED_POSTS is better but I don't like that particular pairing because I can't think of a good name for a companion action type that indicates failure.

The preferred naming should group similar action types together.

const Actions = {  LOAD_POSTS_REQUEST: 'LOAD_POSTS_REQUEST',  LOAD_POSTS_FAILURE: 'LOAD_POSTS_FAILURE',  LOAD_POSTS_SUCCESS: 'LOAD_POSTS_SUCCESS',};

Naming Simple Action Creators

Simple action creators are most analogous to effects and should be named accordingly. By simple action creators I mean functions that produce a single Redux action to be consumed directly by the store. For example,

const loadPostsRequest = subreddit => ({  type: Actions.LOAD_POSTS_REQUEST,  payload: subreddit});const loadPostsSuccess = posts => ({  type: Actions.LOAD_POSTS_SUCCESS,  payload: posts});const loadPostsFailure = err => ({  type: Actions.LOAD_POSTS_FAILURE,  payload: err,  error: true});

Naming Complex Action Creators

On the other hand, some action creators produce actions indirectly through Redux middleware. These are what I call complex action creators.

Complex action creators (created by redux-thunk, redux-promise, etc) are more likely to be the agents of change in your system -- i.e. they affect your state. These should be named with present-tense verbs. For example, loadPosts() is a thunk that changes state.

const loadPosts = subreddit => dispatch => {  // Tell Redux we're now loading...  dispatch(loadPostsRequest(subreddit));  fetch(subreddit).then(posts =>    // Tell Redux we succeeded    dispatch(loadPostsSuccess(posts));  }, err => {    // Tell Redux we failed    dispatch(loadPostsFailure(err));  });};

Adding Todos... Asynchronously

What if the TodoMVC example was sending new todos to a server and needed to be acknowledged by the server before being added locally? Then ADD_TODO/addTodo() is insufficient. Instead, you may want to consider { TODO_ADD_REQUESTED, TODO_ADD_SUCCEEDED, and TODO_ADD_FAILED }.

Or maybe { ADD_TODO_REQUESTED, ADD_TODO_SUCCEEDED, and ADD_TODO_FAILED } depending on what sounds more natural to you and whether you want to group things by noun or by verb.

Recap

Name your action types and creators in a way that is intuitive / non-misleading and -- most importantly -- be consistent.

You Might Also Like...

Level up Your React + Redux + TypeScript

for free with articles, tutorials, sample code, and Q&A.