Methods for tracking action status in Redux
Tags: react, redux, javascript
Published: 2019-08-21 13:26:36

---
title: Methods for tracking action status in Redux
published: true 
tags: react, redux, javascript
canonical_url: https://blog.logrocket.com/tracking-action-status-in-redux/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/ggsfhvhs98zkr44cnyfv.jpeg
---
**Written by [Maciej Cieślar](https://blog.logrocket.com/author/maciejcieslar/)**✏️

Having worked on a fair share of React and Redux applications, I can’t help but notice that many people have a hard time indicating to the user that a given action is currently taking place.

Let’s consider the following example:

```jsx
class RegisterForm extends React.Component {
 state = {
   isLoading: false
 }

 async handleSubmit(event) {
   event.preventDefault();

   this.setState({
     isLoading: true,
   });

   try {
     const result = await post('localhost:8000/api/users', {
       data: {},
     });
   } catch (error) {
     // do something with the error
   }

   // do something with the result
   this.setState({ isLoading: false });
 }

 render() {
   return (
     <form onSubmit={this.handleSubmit.bind(this)} >
       <input type="text" />
       <button type="submit">Submit</button>
       {this.state.isLoading && <p>Spinner!</p>}
     </form>
   );
 }
}
```

Here we have a simplified React register form that should display a loading indicator — say, a spinner — once the user has hit the submit button. Well, we could simply make the request inside the component and use `setState` to keep track of its status, and that would work just fine.

This solution has two problems, however. First, the request and its logic are defined inside a component; we would need to repeat this very same code should we want the same functionality elsewhere in our application.

Second, what if we wanted to display the spinner outside the component? How would we go about lifting that component’s state a few components up?

Here is where Redux comes to our aid.

By having an immutable global state available everywhere in our app, we can save the action’s status inside the state and have it available anywhere — thus, the indicator can be displayed anywhere. Let’s take a look at the usual asynchronous flow of actions in Redux.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## The usual asynchronous action flow

Actions in Redux are objects and, as such, are dispatched synchronously. But thanks to various middleware, we can dispatch them in an asynchronous manner.

There are many libraries that allow us to dispatch actions asynchronously — [redux-thunk](https://github.com/reduxjs/redux-thunk), [redux-saga](https://github.com/redux-saga/redux-saga), and [redux-observable](https://github.com/redux-observable/redux-observable), to name a few.

The usual flow goes like this: first, we dispatch the action that is supposed to set things in motion (usually the action’s type ends with a `_REQUEST` suffix, e.g., `GET_USER_REQUEST`).

Then, somewhere in our state, we make a note that the action is pending, like this:

```jsx
{
  isLoading: true
}
```

Or:

```jsx
{
  pending: true
}
```

> Note: I prefer the name pending because it doesn’t imply that the action is necessarily loading something.

Then, once the action is finished, we dispatch one of the following actions, depending on the outcome: `GET_USER_SUCCESS` or `GET_USER_FAILURE`.

Both of these actions will set the `pending` value to `false` and save (somewhere in the state) either the error or the result.

## The simplest solution for storing the pending indicator

One common approach to handling the loading states of actions is to create a state of the following shape:

```jsx
{
  user: {
    isLoading: true,
    user: {
      ...
    }
    token: '...'
  }
}
```

We can see here that we have a user section where we store all the user-related data.

This solution works well only for the most basic applications, and here’s why: What does `isLoading` tell us, exactly? There are many actions that may be considered user-related, such as registering, logging in, and updating; with this solution, we have no way of differentiating between them.

## Each action on its own

A better approach to handling actions’ pending states is to create a separate object for each action we have.

Here’s an example:

```jsx
{
  user: {
    register: {
      pending: false,
      error: null,
    },
    login: {
      pending: false,
      error: null,
    },
  }
}
```

This way, we can track a given action’s state throughout the whole application or identify specific actions as they occur. This allows us to display the `register` action’s state in multiple places in the application.

While a state like this is much more manageable, this solution still needs a lot of boilerplate code to be written for each action. Let’s consider a different approach, where we create a separate reducer for the pending indicators.

## Creating a separate reducer

In Redux, each dispatched action executes all the reducers, regardless of whether a given reducer is even supposed to handle it.

By creating a separate reducer dedicated to keeping the pending states, we can use the `SUCCESS` and `FAILURE` actions to save the errors and results in other parts of the state.

### Creating the reducer

Since the reducer will be executed on every action, we should filter out those we are not interested in: actions whose type doesn’t end with `_REQUEST`, `_SUCCESS`, or `_FAILURE`.

Since our convention is to name actions like `GET_USERS_REQUEST`, we can create a function called `getActionName`, in which we split the name at the `_` character, remove the last part (`REQUEST`, `SUCCESS`, or `FAILURE`), and join the remaining parts with `_`.

```jsx
function getActionName(actionType) {
 if (typeof actionType !== 'string') {
   return null;
 }

 return actionType
   .split("_")
   .slice(0, -1)
   .join("_");
}
```

If `actionType` is something other than a string, like a commonly used Symbol, we return `null` to avoid an error.

This way, we turn `GET_USERS_REQUEST` into `GET_USERS` and thus have a name under which we can save the pending state in the state.

Here’s the code for the reducer:

```jsx
const pendingReducer = (state = {}, action) => {
 const { type } = action;
 const actionName = getActionName(type);

 if (!actionName) {
   return {
     ...state,
   }
 }

 if (type.endsWith("_REQUEST")) {
   return {
     ...state,
     [actionName]: {
       pending: true
     }
   };
 }

 if (type.endsWith("_SUCCESS") || type.endsWith("_FAILURE")) {
   return {
     ...state,
     [actionName]: {
       pending: false
     }
   };
 }

 return {
   ...state
 };
};
```

First, we check whether the action’s type ends with `_REQUEST`. If that is indeed the case, we create a new entry in the state with the action’s name as a key and `{ pending: true }` as a value.

Then, if the action’s type ends with `_SUCCESS `or `_FAILURE`, we do the same thing, but this time we set `{ pending: false }` as a value.

Now, should we want a user reducer, we can create it like so:

```jsx
const userReducer = (state = initialUserState, action) => {
 if (action.type === GET_USERS_SUCCESS) {
   return {
     ...state,
     user: action.payload.user,
     error: null
   };
 }

 if (action.type === GET_USERS_FAILURE) {
   return {
     ...state,
     user: null,
     error: action.payload.error
   };
 }

 return { ...state };
};
```

Now we need not worry about setting `pending: true` on each action and then setting it back to `false` on success/failure.

> Note: We don’t have error handling here, but it could also be done in a separate reducer.

Here’s a live demo for you to play with:

{% codesandbox 5lc1w module=%2Fsrc%2Findex.js %}

## Summary

Assigning each action its own state to keep track of status is a scalable solution that relies on a lot of boilerplate code. By creating a separate reducer to handle the logic of managing status, we can reduce the amount of redundant code, but in turn, we lose the flexibility to define some additional fields needed to more accurately track a specific action’s status.
* * *
**Editor's note:** Seeing something wrong with this post? You can find the correct version [here](https://blog.logrocket.com/tracking-action-status-in-redux/).

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *
The post [Methods for tracking action status in Redux](https://blog.logrocket.com/tracking-action-status-in-redux/) appeared first on [LogRocket Blog](https://blog.logrocket.com).