Как отправить действия из обработчика ошибок и сразу же разорвать остальную часть цепочки Observable в redux-observable?

Чтобы обработать 401 неавторизованный запрос, я попытался обернуть все свои запросы API с помощью errorHandler:

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

В идеале я мог бы использовать его следующим образом, зная, что 401 вызовет unauthorizedAction(), а, скажем, 500 будет обрабатываться handleOtherErrors.

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

Однако с приведенной выше настройкой проблема возникает, когда я получаю ответ 401. Как и ожидалось, errorHandler возвращает неавторизованное действие, но затем продолжает отображать действие и пытаться что-то сделать с ответом, вместо того, чтобы отправлять это действие и завершать цепочку Observable.

Итак, мой вопрос: как я могу сделать что-то подобное в своем обработчике ошибок?

.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);

person Anton Abilov    schedule 07.03.2018    source источник


Ответы (1)


Операторы — это цепочка наблюдаемых, которые не знают, как и что делают предыдущие или последующие операторы. Можно создать свою собственную серию операторов, обладающих такими знаниями и мощью, но я не рекомендую это делать, так как это противоречит традиционному шаблону RxJS.

Вместо этого вы можете передать селектор результатов, который будет применен к ним с помощью map.

// 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);
    });

Затем его можно было бы использовать следующим образом:

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

Когда создатель действия имеет ту же подпись, что и resultSelector, вы даже можете просто передать его как есть.

apiGet(something, doSomethingWithResponse)
  .catch(handleOtherErrors)
person jayphelps    schedule 07.03.2018
comment
Отлично, я должен был это увидеть, но спасибо за быстрый ответ! Я надеялся, что ты придешь :-D - person Anton Abilov; 08.03.2018