As your app grows, managing string constants for types and wiring up actions to reducers by hand can be a bit of a grind.

It’s a simple process (part of the beauty of Redux) but it can definitely feel repetitive. Doing simple repetitive things is also where we tend to make mistakes. Not to mention that keeping track of string constants adds a lot of cognitive overhead and is error prone. Finally, this results in quite a bit of code for a rather simple concept like “publish” and “subscribe” to a given state change event.

So lately i’ve been writing actions and reducers with redux-actions and I’ve really enjoyed how it has “reduced” (sorry, easy dad joke opportunity) the redux code footprint.

Show Me The Pain

Ok, so maybe you haven’t experienced this pain point just yet and would like an example of what I’m talking about. Any large Redux codebase that uses vanilla action/reducer types will paint the picture.

Take a look at Wordpress Calypso, a massive Redux based front end.

Hand editing hundreds of action type strings and switch/case statements certainly works… but you can see what I’m talking about at scale.

Using redux-actions

Using redux-actions basically just asks you to adhere to a simple naming convention upfront and benefits future-you by:

  • Freeing you from managing action type constants on each side

  • Removing your switch statements in reducers (just pass a pure function)

  • Increasing your sanity level

Let’s take a look at a super simple action/reducer pairing for setting some data as an array, and appending to it. (we’re assuming that these simple actions are things that n-components will want to subscribe to, therefore in redux)

export function setPostsAction(posts) {
  return {
    type: 'SET_POSTS', posts: posts
  }
}

export function appPostsAction(post) {
  return {
    type: 'APPEND_POST', post: post
  }
}
export function postsReducer(state = [], action) {
  switch (action.type) {
  case "SET_POSTS":
    return action.posts
  case "APPEND_POST":
    return [...state, ...action.post]
  default:
    return state
  }
}

OK, so that doesn’t highlight any of the pain quite like what I mentioned above and linked to, but you get the picture. I’m writing this by hand and now have to implement a switch and manage/remember constants for each action type.

With redux-actions, we can squash that down into a single function call that will return all of our action creators

import { createActions } from 'redux-actions'

export const postsActions = createActions({
  POSTS: {
    SET: posts => ({ posts: posts }),
    APPEND: post => ({ post: post })
  }
})

This POSTS/APPEND hierarchy will spit out namespaced functions, which are easy to remember and dispatch.

Importing the exported postActions object will give us

dispatch(postsActions.posts.append(post))

On the reducer side you’ll simply call handleActions with a hash of handler functions that map to this namespacing generated by createActions

import { postsActions } from '../actions/posts'
import { handleActions } from 'redux-actions'

const initialState = []
const handlers = {
  [postsActions.posts.set]: (state, action) => action.posts,
  [postsActions.posts.append]: (state, action) => [...state, ...action.post]
}

export const postsReducer = handleActions(handlers, initialState)

And with that, we’ve swapped our switch statements for a hash table of actions -> handlers and no longer rely on constant strings.

Parting thoughts

Naming is hard.

Getting everyone to accept a naming convention is a big win. Enforcing it at a code level with something like redux-actions can help squash out conflicts and inconsistencies.

In React and JS land, our tools often come without opinions. However, once we want to pattern-ify something, lightweight libraries like this can give us a big ROI.


Matt Carbone

Your host at Hypertext.io @mattcarbone