One dirty redux devtools logging trick

  • redux
  • javascript
posted on 8th October 2020 ∙ by Hynek Svacha ∙ 3 min read

While redux is far from being the coolest kid in the playground at the moment (late 2020), I am still using it on some of my projects. There is still one feature that most of its competitors (like zustand, jotai, recoil, react-query or SWR) lack so far: mature and feature-rich devtools.

This article is not up-to-date. While the proposed trick is still valid, I can’t see any reason to keep using vanilla Redux in 2022. 💡 I would suggest checking out Redux Toolkit, which yields a much better DX (unfortunately, you can’t use the proposed trick here).

But even on redux-devtools, there was something that made me think, “meh…”, and that thing is logging constants. Even though I made my redux constants less GENERATED_CODE_LOOKALIKE and more human readable (thanks mostly to ducks for inspiration), I’d like to be able to parametrize them.

// These constants are quite pleasant to read, but they are still, well… constant. 
const FETCH_PROJECTS_OF_SINGLE_YEAR_INITIALIZED = "[projects] Fetching projects of single year has started"
const FETCH_PROJECTS_OF_SINGLE_YEAR_SUCCESS = "[projects] Fetching projects of single year was successful"
const FETCH_PROJECTS_OF_SINGLE_YEAR_FAILURE = "[projects] Fetching projects of single year has failed"

On the devtools pane, it looks OK; not great, not terrible:

Image of redux devtools (neutral)

However, if I dispatch a lot of these actions in a row, I can’t help but think that something is missing here:

Image of redux devtools without parameters (not optimal result)

As it turned out, it would be quite easy! Just turn your constants into functions that receive an argument and return a string utilizing it:

const FETCH_PROJECTS_OF_SINGLE_YEAR_INITIALIZED = year =>
  `[projects] Fetching projects of year ${year} has started`
const FETCH_PROJECTS_OF_SINGLE_YEAR_SUCCESS = year =>
  `[projects] Fetching projects of year ${year} was successful`
const FETCH_PROJECTS_OF_SINGLE_YEAR_FAILURE =  year =>
  `[projects] Fetching projects of year ${year} has failed`

Well, OK, then, but what about action reducers? No worries! In the case of actions, just make their type property a function call and pass your argument in:

const fetchProjectsOfSingleYearInit = year => ({
  // Note 'type' is a function call
  type: FETCH_PROJECTS_OF_SINGLE_YEAR_INITIALIZED(year),
  year,
})
const fetchProjectsOfSingleYearSuccess = (projectsOfSingleYear, year) => ({
  // Note 'type' is a function call
  type: FETCH_PROJECTS_OF_SINGLE_YEAR_SUCCESS(year),
  projectsOfSingleYear,
  year,
})
const fetchProjectsOfSingleYearFailure = (error, year) => ({
  type: FETCH_PROJECTS_OF_SINGLE_YEAR_FAILURE(year),
  error,
})

As for the reducers, there is the same pattern: make your case a function call and pass your argument. It will match as expected:

export default function reducer(state = {}, action = {}) {
  switch (action.type) {
    case FETCH_PROJECTS_OF_SINGLE_YEAR_INITIALIZED(action.year):
      return {
        //...
      };
    case FETCH_PROJECTS_OF_SINGLE_YEAR_SUCCESS(action.year):
      return {
        //...
      };
    case FETCH_PROJECTS_OF_SINGLE_YEAR_FAILURE(action.year):
      return {
        //...
      };
    default:
      return state;
  }
}

And voilà, your redux devtools actions console is now much more informative (and fun):

Image of redux devtools, parametrized

I really appreciate this when debugging a lot of actions of the same type dispatched with different parameters. More than that, it can reveal possible bugs caused by unexpected arguments passed into actions (e.g., react-router params from a mistyped url).

Image of redux devtools without with a lot of actions of the same type being dispatched with different parameters, parametrized

The idea of making constants dynamic may sound like heresy to some hardcore Redux developers. I would argue that:

  • The pattern is deterministic, with only pure functions used.
  • It is meant to be used rather temporarily, for the purpose of debugging.

So far, I haven’t encountered any issues using it.

👍 Enjoy!


If you find anything in this post that should be improved (either factually or in language), feel free to edit it on Github .