Anton Abilov
Anton Abilov

Reputation: 343

How to dispatch actions from an error handler and immediately break the rest of the Observable chain in redux-observable?

To handle 401 unauthorized requests, I've tried to wrap all of my api requests with an errorHandler:

const apiGet (props) = ajax({
  headers,
  method: 'GET',
  ...props
}).catch(res => {
  if (res.status === 401)
    return Observable.of(unauthorizedAction();
  else
    return Observable.throw(res);
});

Ideally, I could use it in the following way, knowing that a 401 would trigger unauthorizedAction(), while say a 500 would be handled by handleOtherErrors.

action.ofType(SOME_ACTION) => 
  apiGet(something)
    .map(res => doSomethingWithResponse(res))
    .catch(handleOtherErrors);

However, with the setup above, the problem arises when I get a 401 response. The errorHandler returns unauthorizedAction as expected, but then it continues to map the action and try to do something with the response, instead of dispatching that action and terminating the Observable chain.

So, my question is, how could I do something like this in my error handler?

.catch(res => {
  if (res.status === 401)
    // Dispatch an action, but stop the rest of the chain
  else
    return Observable.throw(res);
}).map(...restOfChain)
  .catch(additionalErrorHandlerForNon401);

Upvotes: 1

Views: 71

Answers (1)

jayphelps
jayphelps

Reputation: 15401

Operators are a chain of Observables that have no knowledge of how or what previous or later operators do. It's possible to create your own custom series of operators that would have such knowledge and power, but I don't recommend it as it goes against the traditional RxJS pattern.

Instead, you could pass along a result selector that will be applied using map for them.

// provides a default `resultSelector` that is basically a noop
const apiGet = (props, resultSelector = res => res) =>
  ajax({ headers, method: 'GET', ...props })
    .map(res => resultSelector(res))
    .catch(res => {
      if (res.status === 401)
        return Observable.of(unauthorizedAction());
      else
        return Observable.throw(res);
    });

It could then be used like this:

apiGet(something, res => doSomethingWithResponse(res))
  .catch(handleOtherErrors)

When the action creator has the same signature as the resultSelector you could even just pass it as-is

apiGet(something, doSomethingWithResponse)
  .catch(handleOtherErrors)

Upvotes: 3

Related Questions