Search code examples
rxjsrxjs5redux-observable

ReduxObservable cancellation based on action type and its data


I have React app which uses redux-observable with typescript. In this scenario, FetchAttribute Action gets triggered with a id and then make an ajax call. In certain case, I would want to cancel the ajax request if "FETCH_ATTRIBUTE_CANCEL" action was triggered with the same id as of "FetchAttributeAction" action.

action$.ofType(FETCH_ATTRIBUTE)
    .switchMap((request: FetchAttributeAction) => {

      return ajax.getJSON(`/api/fetch-attribute?id=${request.id}`)
        .flatMap((fetchUrl) => {
            // return new action
        })
        .takeUntil(action$.ofType(FETCH_ATTRIBUTE_CANCEL));
    });

interface FetchAttributeAction{
  id: number;
}

Problem: How do we cancel the execution based on action type + action data? In my case, it would FETCH_ATTRIBUTE_CANCEL and id.


Solution

  • The key is to filter actions in the takeUntil notifier to only those which match the ID you care about.

    action$.ofType(FETCH_ATTRIBUTE_CANCEL).filter(action => action.id === request.id)

    So here's what it might look like:

    Demo: https://stackblitz.com/edit/redux-observable-playground-xztkoo?file=fetchAttribute.js

    const fetchAttributeEpic = action$ =>
      action$.ofType(FETCH_ATTRIBUTE)
        .mergeMap(request =>
          ajax.getJSON(`/api/fetch-attribute?id=${request.id}`)
            .map(response => fetchAttributeFulfilled(response))
            .takeUntil(
              action$.ofType(FETCH_ATTRIBUTE_CANCEL).filter(action => action.id === request.id)
            )
        );
    

    You can also take a look at previous questions:


    The OP also pointed out that they were using switchMap (as did I originally when I copied their code) which would have meant that the epic only ever had one getJSON at a time since switchMap will unsubscribe from previous inner Observables. So that also needed to be chained. Good catch!