Redux axios request cancellation

Viewed 1618

I have a React Native application with Redux actions and reducers. I'm using the redux-thunk dispatch for waiting the asyncron calls. There is an action in my application:

export const getObjects = (id, page) => {
    return (dispatch) => {
        axios.get(`URL`)
            .then(response => {
                dispatch({ type: OBJECTS, payload: response });
            }).catch(error => {
                throw new Error(`Error: objects -> ${error}`);
            });
    };
};

That's working properly, but sometimes the user click on the back button before the action finished the request, and I must cancel it. How can I do it in a separated action? I read this, but I didn't find any option in axios for abort. I read about the axios cancellation, but it's create a cancel method on the function scope and I can't return, because the the JS don't support multiple returns.

What is the best way to cancel axios request in an other Redux action?

1 Answers

I would recommend using something like RxJS + Redux Observables which provides you with cancellable observables.

This solution requires a little bit of learning, but I believe it's a much more elegant way to handle asynchronous action dispatching versus redux-thunk which is only a partial solution to the problem.

I suggest watching Jay Phelps introduction video which may help you understand better the solution I'm about to propose.

A redux-observable epic enables you to dispatch actions to your store while using RxJS Observable functionalities. As you can see below the .takeUntil() operator lets you piggyback onto the ajax observable and stop it if elsewhere in your application the action MY_STOPPING_ACTION is dispatched which could be for instance a route change action that was dispatched by react-router-redux for example:

import { Observable } from 'rxjs';

const GET_OBJECTS = 'GET_OBJECTS';
const GET_OBJECTS_SUCCESS = 'GET_OBJECTS_SUCCESS';
const GET_OBJECTS_ERROR = 'GET_OBJECTS_ERROR';
const MY_STOPPING_ACTION = 'MY_STOPPING_ACTION';

function getObjects(id) {
  return {
    type: GET_OBJECTS,
    id,
  };
}

function getObjectsSuccess(data) {
  return {
    type: GET_OBJECTS_SUCCESS,
    data,
  };
}

function getObjectsError(error) {
  return {
    type: GET_OBJECTS_ERROR,
    data,
  };
}

const getObjectsEpic = (action$, store) = action$
  .ofType(GET_OBJECTS)
  .switchMap(action => Observable.ajax({
      url: `http://example.com?id=${action.id}`,
    })
    .map(response => getObjectsSuccess(response))
    .catch(error => getObjectsError(error))         
    .takeUntil(MY_STOPPING_ACTION)
  );
Related