redux-thunk和处理调度结果中的异常

时间:2017-05-12 03:21:04

标签: reactjs asynchronous promise redux redux-thunk

我有一个基本的thunk动作创建器和reducer改编自Redux文档:http://redux.js.org/docs/advanced/AsyncActions.html

// action creator

function fetchPosts () {
  return dispatch => {
    dispatch({ type: 'FETCH_POSTS_REQUEST' })

    return fetch('http://jsonplaceholder.typicode.com/posts')
      .then(response => response.json())
      .then(json => dispatch({ type: 'FETCH_POSTS_SUCCESS', items: json }))
      // THIS CATCHES FETCH REQUEST ERRORS, AND COMPONENT LEVEL ERRORS
      .catch(error => dispatch({ type: 'FETCH_POSTS_FAILURE', error: error.message }))
  }
}

// reducer

function reducer (state = { isFetching: false, items: [] }, action) {
  switch (action.type) {
    case 'FETCH_POSTS_REQUEST':
      return Object.assign({}, state, { isFetching: true })
    case 'FETCH_POSTS_SUCCESS':
      return Object.assign({}, state, { isFetching: false, items: action.items })
    case 'FETCH_POSTS_FAILURE':
      return Object.assign({}, state, { isFetching: false })
    default:
      return state
  }
}

在作为props传递状态的React组件中,我检查是否存在帖子项,如果存在则强制组件级别错误:

const Test = props => {
  if (!props.items.length) return null
  throw new Error('Error!')
}

启动应用时:

  1. fetchPosts动作创建者被称为
  2. 发出HTTP请求,并在响应之后调度 FETCH_POSTS_SUCCESS 操作。
  3. 组件现在使用状态中生成的进行更新,并尝试阅读props.invalidProperty.error
  4. 这会导致JS异常:Cannot read property 'error' of undefined
  5. 到目前为止一切顺利。

    问题是组件中的JS异常永远不会输出到控制台。相反,获取承诺的catch()块会捕获错误,并调度 FETCH_POSTS_FAILURE 操作。

    这会吞噬受更新商店影响的组件中的所有错误。调度 FETCH_POSTS_FAILURE 状态更改,但这感觉不正确 - 实际获取帖子时没有错误,但使用这些帖子的组件中有下游错误。

    我正在寻找一种模式来帮助将异步请求中的错误与因通过调度更改状态而发生的任何其他随机错误分开。

    修改

    Redux github repo中的异步示例示例:https://github.com/nandastone/redux/commit/88ab48040ce41c39d8daba8cc0c13a6f32c38adf#diff-eeb827d44ad03655e63b7e9319a03dd4R6

3 个答案:

答案 0 :(得分:3)

Promise.catch处理程序还会捕获从解析或拒绝处理程序抛出的任何错误。

fetch('http://jsonplaceholder.typicode.com/posts').then(res => {
  throw new Error();
}).catch(err => {
  //will handle errors from both the fetch call and the error from the resolution handler
});

要仅处理来自fetch的错误,并确保解析处理程序中dispatch({ type: 'FETCH_POSTS_SUCCESS', items: json })的调用引发的任何错误未在catch处理程序中捕获,请附加拒绝处理程序到fetch

return fetch('http://jsonplaceholder.typicode.com/posts').then(response => response.json, error => {
    dispatch({ type: 'FETCH_POSTS_FAILURE', error: error.message });
}).then(json => dispatch({ type: 'FETCH_POSTS_SUCCESS', items: json }), error => {
    //response body couldn't be parsed as JSON
});

fetch不会将状态代码> = 400视为错误,因此只有在出现网络或CORS错误时才会拒绝上述调用,这就是必须在解析处理程序中检查状态代码的原因

function fetchHandler(res) {
  if (res.status >= 400 && res.status < 600) {
    return Promise.reject(res);
  }
  return res.json();
}



return fetch('http://jsonplaceholder.typicode.com/posts').then(fetchHandler, error => {
    //network error
    dispatch({ type: 'NETWORK_FAILURE', error });
}).then(json => dispatch({ type: 'FETCH_POSTS_SUCCESS', items: json }), error => {
    dispatch({ type: 'FETCH_POSTS_FAILURE', error: error.message });
});

请注意,React组件中引发的任何错误都可能使React处于不一致状态,从而阻止后续render并使应用程序对UI事件无响应。 React Fiber通过错误边界解决了这个问题。

答案 1 :(得分:0)

您可以考虑将错误处理程序移动到之前的then块中。

我写了一个原理的简单演示:https://codepen.io/anon/pen/gWzOVX?editors=0011

const fetch = () => new Promise((resolve) => {
  setTimeout(resolve, 100);
});

const fetchError = () => new Promise((resolve, reject) => {
  setTimeout(reject, 200)
});

fetch()
  .then(() => { throw new Error("error") })
  .catch(() => { console.log("error in handler caught") })

fetch()
  .then(() => { throw new Error("error") }, 
        () => { console.log("error in handler not caught") })

fetchError()
  .then(() => { throw new Error("error") })
  .catch(() => { console.log("error in fetch caught 1") })

fetchError()
  .then(() => { throw new Error("error") }, 
        () => { console.log("error in fetch caught 2") })

答案 2 :(得分:0)

这是我写的一个fetch包装器的代码块。您会在checkStatus承诺链中看到executeRequest,我正在使用response.ok检查任何非2xx响应。由于我的API错误返回JSON,我将任何非2xx响应传递给parseResponse,然后将reject()传递给已解析的错误数据,而错误数据又被拒绝并由{{1}错误地返回}:

executeRequest