Cancelling previous async action using redux-thunk

I was recently faced with the same problem, in which I had to cancel pending or stale async redux actions. I solved it by creating a cancel redux actions middleware.

In redux we have the concepts of middlewares. So when you are sending the request it will go through a list of middlewares. Once the api responds back its response will pass through a number of middlewares before it reaches redux store and eventually your UI.

Now suppose we have written a cancel middleware. The api request will go through this middleware when it being initiated and the api response will also go through this middleware when the api responds back.

Each api request in redux is associated with an action, each action has a type and payload.

Write a middleware, which whenever an api request is done stores the action type associated. Along with it, it stores a flag which states whether to discard this action. If an action of similar type is fired again, make this flag true which says discard this action. When the api response comes for the previous action since the discard flag for this api request has been set to true, send null as response from the middleware.

Look at this blog for detailed information about this approach.

https://tech.treebo.com/redux-middlewares-an-approach-to-cancel-redux-actions-7e08b51b83ce


One approach would be to mark those requests as canceled by giving them random id and checking its status before handling the result.

The way to do this is to assign random id for this call in your first dispatch (inside the thunk) and check it in the reducer before handling the result.

const actionId = Math.random();
dispatch({type: AJAX_LOAD_CONST, id:actionId })

When you want to cancel all of the request use

dispatch({type:HANDLE_AJAX_RESPONSE, id:actionId, results: json })

When you want to handle the results don't forget to send the id that you u

and in the reducer have something like this:

function reducer(state = initialState, action) {
  switch (action.type) {
    case actions.AJAX_LOAD_CONST:
      return Object.assign({}, state, { ajax: state.ajax.concat(action.id) });
    case actions.CANCEL_ALL_AJAX:
      return Object.assign({}, state, { ajax: [] });
    case actions.HANDLE_AJAX_RESPONSE:
      if (state.ajax.includes(action.id) {
        //return state reduced with action.results here
      }
      return state;
  }
}

If you use XMLHttpRequest or one of it's wrappers (JQuery?) you can also store the requests themselves and call request.abort(). if you use the new fetch api you do not have this luxury as promises lack this behavior.


If you're using jquery ajax, you can make your action creator return the promise, it will be returned by the dispatch function, then it'll be possible to abort it. Here is an example :

Action creator

function doSomething() {
  return (dispatch) => {
    return $.ajax(...).done(...).fail(...);
  }
}

Your component

  componentDidMount(){
    this.previousPromise = this.props.dispatch(doSomething());
  }

  somefnct() {
    this.previousPromise.abort();
  }