如何使用 Redux Thunk 处理 fetch() 响应中的错误?
How to handle errors in fetch() responses with Redux Thunk?
我正在使用同构提取发出 API 请求,并使用 Redux 处理我的应用程序状态。
我想通过触发 Redux 操作来处理互联网连接丢失错误和 API 错误。
我有以下(进行中/错误)代码,但无法找出触发 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 Actions(一个一般错误,一个特定于我们在错误时执行的操作发生了)。
我想要实现的目标是否可行?
似乎(通过我登录到控制台)脚本的 'then' 部分仍在执行(因为它的内容是我的 'catch' 调度函数)..
解决方案很简单(对于错误记录的两个实例)替换:
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)
])
我对几件事感到困惑:
- 为什么要围绕分派两个同步操作使用
Promise.all
?用 {type: PRE_FETCH_RESOURCES_FAIL, ...}
之类的东西调用 dispatch
不会 return Promise,所以 Promise.all
是不必要的。 Promise.all()
仅当您发送的动作 本身 被编写为 thunk 动作创建者时才有用,这里不是这种情况。
return dispatch => ...
仅在动作创建者的最开始时需要一次。无需在 catch
或 then
块中重复此操作——实际上,重复它会使内部代码根本不执行。这是一种将 dispatch
注入顶层函数的方法,重复它没有意义。
- 如果你把
then
放在 catch
之后,它会 运行 即使在错误被捕获之后。这不是您想要的行为——在错误处理程序之后 运行 成功处理程序没有意义。您希望它们成为两个独立的代码路径。
- 次要的命名挑剔:您将响应称为“
req
”。应该是res
.
感觉你对 Redux Thunk 的工作原理有错误的心理模型,并试图将不同示例的各个部分组合在一起,直到它点击为止。随机缩进也导致此代码有点难以理解。
这在未来会很痛苦,所以我建议获得一个更完整的心智模型,了解 Redux Thunk 的作用、return dispatch => ...
的含义以及 Promises 如何融入画面。我会推荐这个答案作为 .
如果我们解决了这些问题,您的代码应该大致如下所示:
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'})
}
);
}
}
我正在使用同构提取发出 API 请求,并使用 Redux 处理我的应用程序状态。
我想通过触发 Redux 操作来处理互联网连接丢失错误和 API 错误。
我有以下(进行中/错误)代码,但无法找出触发 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 Actions(一个一般错误,一个特定于我们在错误时执行的操作发生了)。
我想要实现的目标是否可行?
似乎(通过我登录到控制台)脚本的 'then' 部分仍在执行(因为它的内容是我的 'catch' 调度函数)..
解决方案很简单(对于错误记录的两个实例)替换:
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)
])
我对几件事感到困惑:
- 为什么要围绕分派两个同步操作使用
Promise.all
?用{type: PRE_FETCH_RESOURCES_FAIL, ...}
之类的东西调用dispatch
不会 return Promise,所以Promise.all
是不必要的。Promise.all()
仅当您发送的动作 本身 被编写为 thunk 动作创建者时才有用,这里不是这种情况。 return dispatch => ...
仅在动作创建者的最开始时需要一次。无需在catch
或then
块中重复此操作——实际上,重复它会使内部代码根本不执行。这是一种将dispatch
注入顶层函数的方法,重复它没有意义。- 如果你把
then
放在catch
之后,它会 运行 即使在错误被捕获之后。这不是您想要的行为——在错误处理程序之后 运行 成功处理程序没有意义。您希望它们成为两个独立的代码路径。 - 次要的命名挑剔:您将响应称为“
req
”。应该是res
.
感觉你对 Redux Thunk 的工作原理有错误的心理模型,并试图将不同示例的各个部分组合在一起,直到它点击为止。随机缩进也导致此代码有点难以理解。
这在未来会很痛苦,所以我建议获得一个更完整的心智模型,了解 Redux Thunk 的作用、return dispatch => ...
的含义以及 Promises 如何融入画面。我会推荐这个答案作为
如果我们解决了这些问题,您的代码应该大致如下所示:
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'})
}
);
}
}