Как обрабатывать ошибки в ответах 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 ответа
Я запутался в нескольких вещах:
- Почему вы используете
Promise.all
вокруг отправки двух синхронных действий? призваниеdispatch
с чем-то вроде{type: PRE_FETCH_RESOURCES_FAIL, ...}
не вернет обещание, такPromise.all
не нужноPromise.all()
полезно только в том случае, если отправляемые вами действия сами по себе записаны как создатели thunk action, а здесь это не так. return dispatch => ...
Нужно только один раз в самом начале действия создателей. Нет необходимости повторять это вcatch
или жеthen
блоки - на самом деле, повторяя его, внутренний код вообще не выполняется. Это способ введенияdispatch
в вашу функцию на верхнем уровне, и нет смысла повторять это.- Если вы положите
then
послеcatch
, он будет работать даже после того, как ошибка была обнаружена. Это не то поведение, которое вам нужно - нет смысла запускать обработчик успеха сразу после обработчика ошибок. Вы хотите, чтобы они были двумя отдельными путями кода. - Незначительный придурок: вы называете ответ "
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)
])