Promises within your Redux code

You’ve created your Redux reducer code and you want to get data from a remote location using fetch or axios etc. or for that matter any functionality that runs asynchronously and returns a Promise.

Let’s look at an example of some code contrived code which hopefully makes things a little clearer

export const queryDataSource = (query: string) => {
  return {
    type: ActionType.Query,
    payload: new Promise<any>((resolve, reject) => {
      // simulate a async query
      setTimeout(() => {
        resolve();
      }, 3000);
    });
  };
}

In this example code, I wanted to make it really obvious what was going on under the hood, hence we return a Promise as the payload and the promise could encapsulate a server call, but in this case we”ll simulate this with a simple timeout.

We’d now normally handle this in a switch statement like this

  switch(action.type) {
    case ActionType.Query:
      return {
        ...state,
        data: action.payload
      };

However this isn’t what we really want to happen as the payload is a Promise. What we want is to handle the change in Promise state in an asynchronous way.

Most likely we’ll want a flag in our redux data which is bound to a property that alerts the user when an asynchronous operation starts (i.e. shows a progress indicator), then alerts the user to the operation completing and also displays errors in the operation fails and ofcourse stores the results of the operation into the redux store.

This is all fairly simple to do in code using a Promise and with redux-promise-middleware it’s equally simple to do with redux. This middleware converts the Promise into three separate events which can be handled within the redux switch function.

To add redux-promise-middleware, we do the following

  • Run yarn add redux-promise-middleware
  • Next we need to add the promise middle ware to our store creation, for example using applyMiddleware(promiseMiddleware) such as the example below
    import promiseMiddleware from 'redux-promise-middleware';
    
    const store = applyMiddleware(promiseMiddleware)(createStore)(
      combineReducers({
        queryReducer,
        connectionReducer
      })
    );
    

The promise middleware will now change the name of our actions, for example assuming ActionType.Query (in the code earlier) is the string “QUERY” then the promise middleware suffixes this string with _PENDING (when the promise is first returned), _FULFILLED (when the promise successfully completes) and _REJECTED (when the promise fails, i.e. an error).

So it’s easy to write the following to use in the redux switch

const PENDING = actionType => `${actionType}_PENDING`;
const FULFILLED = actionType => `${actionType}_FULFILLED`;
const REJECTED = actionType => `${actionType}_REJECTED`;

Now our switch case statements might look like this

case PENDING(ActionType.Query):
   return {
      ...state,
      queryError: undefined,
      runningQuery: true
   };
case FULFILLED(ActionType.Query):
   return {
      ...state,
      queryResult: action.payload,
      runningQuery: false
   };  
case REJECTED(ActionType.Query):
   return {
      ...state,
      queryError: action.payload,
      runningQuery: false
   };  

In the above the PENDING action type will occur first, so we’ll ensure any error is cleared and set a property which we bind a progress indicator in the UI. If the promise fails, REJECTED is called and we could assign some error data to our store for displaying to the user, obviously FULFILLED is called when a successful completion occurs.