2016-05-06 2 views
15

Я делаю запросы API, используя изоморфную выборку, и используя Redux для управления состоянием моего приложения.Как обрабатывать ошибки в ответах fetch() с помощью Redux Thunk?

Я хочу обрабатывать ошибки потери интернет-соединения и ошибки 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)) 
     }) 
    } 
} 

Если я intionally отключить подключение к интернету, в 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 при возникновении ошибки (общая ошибка и одна конкретная для действия мы повторное выполнение при возникновении ошибки).

Является ли то, что я пытаюсь достичь, даже возможно?

Кажется, что (через мой каротаж в консоль) «затем» часть сценария по-прежнему выполняется (как содержимое него мои функции отправки «ловить») ..

ответ

48

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

  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. Незначительное название nitpick: вы вызываете ответ «req». Вероятно, это должно быть res.

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

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

Ничего себе, спасибо за подробное вскрытие. Чтение введения Redux Thunk »прямо сейчас. Большое спасибо! –

+0

@Dan Abramov Что делать, если я хочу извлечь выборку в отдельное место, вместе с «fetch полностью не удалось (например, cors или timeout timeout/отказался)» catch, но оставить более конкретный улов в действии. Является ли это возможным? –

+0

@ Дана Абрамова - У меня возникают проблемы, связанные с обещанием в этом сценарии. он всегда запускает разрешенную функцию в цепочке вместо отклоненной, когда в thunk она определенно отклоняется с момента отправки моего действия с ошибкой. мысли? –

-1

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

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) 
]) 
+1

И '' Promise.all' и Promise.reject' излишни. Хотя вы можете технически заставить его работать, это делает код более сложным, чем нужно, и не имеет никакой цели. Пожалуйста, см. Мой ответ для деталей и предложение перезаписи. –

Смежные вопросы