ReduxObservable cancellation based on action type

2019-08-26 06:03发布

问题:

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.

回答1:

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:

  • Redux Observable: If the same action is dispatched multiple times, how do I cancel one of them?
  • Independent chain cancellation in redux-observable?
  • Dispatch an action in response to cancellation

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!



回答2:

I think you should be able to make takeUntil selective for a certain action id with pluck and filter.

ex:

.takeUntil(action%.ofType(FETCH_ATTRIBUTE_CANCEL)
.pluck('id')
.filter((cancelActionID) => cancelActionID === fetchID))

The non-obvious part to me is how to get the current fetchID to run that comparison. I might consider try using do to store in a temporary variable