Как обрабатывать ошибки в ответах 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 выполняется (так как ее содержимое является моими диспетчерскими функциями catch)..

2 ответа

Решение

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

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

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

Это будет болезненно в будущем, поэтому вместо этого я предлагаю получить более полную ментальную модель того, что делает Redux Thunk, что return dispatch => ... значит, и как обещания вписываются в картину. Я бы порекомендовал этот ответ в качестве углубленного введения в 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'})
      }
    );
  }
}

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

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)
])
Другие вопросы по тегам