Как обрабатывать ошибки в ответах fetch() с помощью Redux Thunk?

Я делаю запросы API, используя изоморфную выборку, и используя Redux для обработки состояния моего приложения.

Я хочу обрабатывать ошибки потери интернет-соединения и ошибки API, отключая действия Redux.

У меня есть следующий (незавершенный/плохой) код, но он не может определить правильный способ запуска действий Redux (а не просто выбросить ошибку и остановить все):

export function createPost(data = {}) {

    return dispatch => {

        dispatch(requestCreatePost(data))

        return fetch(API_URL + data.type, {
            credentials: 'same-origin',
            method: 'post',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json',
                'X-WP-Nonce': API.nonce
            },
            body: JSON.stringify(Object.assign({}, data, {status: 'publish'}))
        }).catch((err) => {

            //HANDLE WHEN HTTP ISN'T EVEN WORKING
            return dispatch => Promise.all([
                dispatch({type: PRE_FETCH_RESOURCES_FAIL, errorType: 'fatal', message:'Error fetching resources', id: h.uniqueId()}),
                dispatch({type: PRE_CREATE_API_ENTITY_ERROR, errorType: 'fatal', id: h.uniqueId(), message: 'Entity error before creating'})
            ])
        }).then((req) => {

            //HANDLE RESPONSES THAT CONSTITUTE AN ERROR (VIA THEIR HTTP STATUS CODE)
            console.log(req);
            if (!req || req.status >= 400) {
                return dispatch => Promise.all([
                    dispatch({type: FETCH_RESOURCES_FAIL, errorType: 'warning', message:'Error after fetching resources', id: h.uniqueId()}),
                    dispatch({type: CREATE_API_ENTITY_ERROR, errorType: 'warning', id: h.uniqueId(), message: 'Entity error whilst creating'})
                ])
            }
            else {
                return req.json()
            }
        }).then((json) => {
            var returnData = Object.assign({},json,{
                type: data.type
            });
            dispatch(receiveCreatePost(returnData))
        })
    }
}

Если я интуитивно отключу подключение к Интернету, в консоли JS, когда я регистрируюсь через console.log() (как указано выше), он выводит это: POST http://example.com/post net::ERR_INTERNET_DISCONNECTED(anonymous function) (dispatch) { return Promise.all([dispatch({ type: PRE_FETCH_RESOURCES_FAIL, errorType: 'fatal', message: 'Error fetching resources', id: _CBUtils2.default.uniqueId() }), dispatch({ type:… cb_app_scripts.js?ver=1.0.0:27976 Uncaught (in promise) TypeError: req.json is not a function(…)

Простите меня, если это совершенно неправильно, но я не хочу ничего делать, кроме как отключить два действия Redux при возникновении ошибки (общая ошибка и одна конкретная для действия, которое мы выполняли при возникновении ошибки).

Я пытаюсь достичь того, чего я пытаюсь достичь?

Похоже, что (через мой вход в консоль) 'then' часть script все еще выполняется (поскольку содержимое это мои функции отправки "catch" ).

Ответ 1

Я смутился о нескольких вещах:

  • Почему вы используете Promise.all для отправки двух синхронных действий? Вызов dispatch с чем-то вроде {type: PRE_FETCH_RESOURCES_FAIL, ...} не вернет обещание, поэтому Promise.all не нужно. Promise.all() полезен только в том случае, если действия, которые вы отправляете, сами записываются как создатели действия thunk, что здесь не так.
  • return dispatch => ... требуется только один раз в самом начале создателей действия. Нет необходимости повторять это в блоках catch или then - на самом деле, повторяя это, внутренний код вообще не выполняется. Это способ вставить dispatch в вашу функцию на верхнем уровне, и нет смысла его повторять.
  • Если вы поместите then после catch, он будет запущен даже после обнаружения ошибки. Это не то поведение, которое вы хотите - не имеет смысла запускать обработчик успеха сразу после обработчика ошибок. Вы хотите, чтобы они были двумя отдельными кодами кода.
  • Незначительная именования nitpick: вы вызываете ответ "req". Вероятно, это должно быть res.

Похоже, у вас неправильная ментальная модель того, как работает Redux Thunk, и пытаются объединить части разных примеров вместе, пока они не нажмут. Случайный отступ также способствует тому, что этот код немного трудно понять.

Это будет болезненно в будущем, поэтому вместо этого я предлагаю получить более полную ментальную модель того, что делает Redux Thunk, что означает return dispatch => ... и как Promises вписывается в изображение. Я бы рекомендовал этот ответ как углубленное введение в Redux Thunk.

Если мы исправим эти проблемы, ваш код должен выглядеть примерно так:

export function createPost(data = {}) {
  return dispatch => {
    dispatch(requestCreatePost(data));

    return fetch(API_URL + data.type, {
      credentials: 'same-origin',
      method: 'post',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'X-WP-Nonce': API.nonce
      },
      body: JSON.stringify(Object.assign({}, data, {status: 'publish'}))
    })
    // Try to parse the response
    .then(response =>
      response.json().then(json => ({
        status: response.status,
        json
      })
    ))
    .then(
      // Both fetching and parsing succeeded!
      ({ status, json }) => {
        if (status >= 400) {
          // Status looks bad
          dispatch({type: FETCH_RESOURCES_FAIL, errorType: 'warning', message:'Error after fetching resources', id: h.uniqueId()}),
          dispatch({type: CREATE_API_ENTITY_ERROR, errorType: 'warning', id: h.uniqueId(), message: 'Entity error whilst creating'})
        } else {
          // Status looks good
          var returnData = Object.assign({}, json, {
              type: data.type
          });
          dispatch(receiveCreatePost(returnData))
        }
      },
      // Either fetching or parsing failed!
      err => {
        dispatch({type: PRE_FETCH_RESOURCES_FAIL, errorType: 'fatal', message:'Error fetching resources', id: h.uniqueId()}),
        dispatch({type: PRE_CREATE_API_ENTITY_ERROR, errorType: 'fatal', id: h.uniqueId(), message: 'Entity error before creating'})
      }
    );
  }
}

Ответ 2

Решение было просто (для обоих экземпляров журнала ошибок) заменить:

return dispatch => Promise.all([
    dispatch({type: PRE_FETCH_RESOURCES_FAIL, errorType: 'fatal', message:'Error fetching resources', id: h.uniqueId()}),
    dispatch({type: PRE_CREATE_API_ENTITY_ERROR, errorType: 'fatal', id: h.uniqueId(), message: 'Entity error before creating'})
])```

С помощью:

return Promise.all([
    dispatch({type: PRE_FETCH_RESOURCES_FAIL, errorType: 'fatal', message:'Error fetching resources', id: h.uniqueId()}),
    dispatch({type: PRE_CREATE_API_ENTITY_ERROR, errorType: 'fatal', id: h.uniqueId(), message: 'Entity error before creating'}),
Promise.reject(err)
])