简体   繁体   English

如何使用Redux Thunk处理fetch()响应中的错误?

[英]How to handle errors in fetch() responses with Redux Thunk?

I'm making API requests using isomorphic fetch, and using Redux to handle my app's state. 我正在使用isomorphic fetch创建API请求,并使用Redux来处理我的应用程序的状态。

I want to handle both internet connection loss errors, and API errors, by firing off Redux actions. 我想通过触发Redux操作来处理互联网连接丢失错误和API错误。

I have the following (work-in-progress / bad) code, but can't figure out the correct way to fire the Redux actions (rather than just throw an error and stop everything) : 我有以下(工作进行中/坏)代码,但无法找出触发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))
        })
    }
}

If I intionally disable the internet connection, in the JS Console, when I log via console.log() (as above), it's outputting this: 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(…) 如果我无法禁用互联网连接,在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(…)

Forgive me if this is entirely wrong, but I don't want to do anything but fire off two Redux Actions when there is an error (a general error, and one specific to the action we were performing when the error occurred). 请原谅我,如果这是完全错误的,但我不想做任何事情,但是当出现错误时会触发两个Redux动作(一般错误,一个特定于我们在发生错误时执行的操作)。

Is what I'm trying to achieve even possible? 我正在努力实现的目标是什么?

It seems that (via my logging to console) the 'then' part of the script is still being executed (as the contents of it are my 'catch' dispatch functions).. 似乎(通过我的日志记录到控制台)脚本的'then'部分仍在执行(因为它的内容是我的'catch'调度函数)..

I'm confused about several things: 我对几件事感到困惑:

  1. Why do you use Promise.all around dispatching two synchronous actions? 为什么使用Promise.all来调度两个同步动作? Calling dispatch with something like {type: PRE_FETCH_RESOURCES_FAIL, ...} won't return a Promise, so Promise.all is unnecessary. 使用{type: PRE_FETCH_RESOURCES_FAIL, ...}等调用dispatch将不会返回Promise,因此Promise.all是不必要的。 Promise.all() is only useful if the actions you dispatch are themselves written as thunk action creators, which is not the case here. Promise.all()仅在您发送的操作本身被写为thunk动作创建者时才有用,这不是这里的情况。
  2. return dispatch => ... is only necessary once at the very beginning of the action creators. return dispatch => ...只需要在动作创建者的最开始一次。 There is no need to repeat this in the catch or then blocks—in fact, repeating it makes the inner code not execute at all. 没有必要在重复这个catchthen块,其实,重复它使内部的代码不能执行的。 This is a way to inject dispatch into your function at the top level, and there is no point to repeating it. 这是一种将dispatch注入到顶级函数中的方法,没有必要重复它。
  3. If you put then after a catch , it will run even after an error was caught. 如果你把then一后catch ,它将运行被抓获错误,即使之后。 This is not the behavior your want—it doesn't make sense to run the success handler right after the error handler. 这不是您想要的行为 - 在错误处理程序之后立即运行成功处理程序是没有意义的。 You want them to be two separate code paths. 您希望它们是两个独立的代码路径。
  4. Minor naming nitpick: you are calling the response a “ req ”. 次要命名挑剔:您将响应称为“ req ”。 It should probably be res . 它可能应该是res

It feels like you have a wrong mental model of how Redux Thunk works, and are trying to combine parts of different examples together until it clicks. 感觉你有一个关于Redux Thunk如何工作的错误心智模型,并试图将不同示例的部分组合在一起直到它点击。 The random indentation also contributes to this code being a little bit hard to understand. 随机缩进也有助于此代码有点难以理解。

This is going to be painful in the future so instead I suggest to get a more complete mental model of what Redux Thunk does, what return dispatch => ... means, and how Promises fit into the picture. 这将是一个痛苦的未来,所以我建议得到一个更完整的心理模型Redux Thunk做什么,什么return dispatch => ...意思,以及Promise如何适应图片。 I would recommend this answer as an in-depth introduction to Redux Thunk . 我会推荐这个答案作为Redux Thunk的深入介绍

If we fix those problems, your code should look roughly like this instead: 如果我们修复这些问题,您的代码应该大致相同:

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

The solution was simply to (for both instances of error logging) replace: 解决方案只是(对于错误记录的两个实例)替换:

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'})
])```

With: 附:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM