javascript - español - ¿Cómo manejar los errores en las respuestas fetch() con Redux Thunk?
redux-< sagas (2)
Estoy haciendo solicitudes de API usando fetch isomorphic y usando Redux para manejar el estado de mi aplicación.
Deseo controlar los errores de pérdida de conexión a Internet y los errores de API activando acciones de Redux.
Tengo el siguiente código (trabajo en progreso / malo), pero no puedo encontrar la manera correcta de disparar las acciones de Redux (en lugar de simplemente lanzar un error y detener todo):
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))
})
}
}
Si desactivo la conexión a Internet, en la Consola JS, cuando inicio sesión a través de console.log () (como se 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(…)
anteriormente), se está produciendo esto: 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(…)
Perdónenme si esto es completamente incorrecto, pero no quiero hacer otra cosa que disparar dos acciones de Redux cuando hay un error (un error general y uno específico de la acción que estábamos realizando cuando ocurrió el error).
¿Lo que estoy tratando de lograr es posible?
Parece que (a través de mi registro en la consola) la parte ''entonces'' del guión aún se está ejecutando (ya que el contenido del mismo es mis funciones de despacho ''catch'').
La solución fue simplemente para (para ambas instancias de registro de errores) reemplazar:
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''})
])```
Con:
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)
])
Estoy confundido sobre varias cosas:
- ¿Por qué usas
Promise.all
alrededor del envío de dos acciones sincrónicas? Llamar aldispatch
con algo como{type: PRE_FETCH_RESOURCES_FAIL, ...}
no devolverá una Promesa, por lo quePromise.all
es innecesario.Promise.all()
solo es útil si las acciones que distribuye están escritas como creadores de acción thunk, que no es el caso aquí. -
return dispatch => ...
solo es necesario una vez al principio de los creadores de acciones. No hay necesidad de repetir esto en lacatch
o bloques, de hecho, al repetirlo no se ejecuta el código interno. Esta es una forma de insertardispatch
en su función en el nivel superior, y no tiene sentido repetirla. - Si lo pones
then
unacatch
, se ejecutará incluso después de que se haya detectado un error. Este no es el comportamiento que desea, no tiene sentido ejecutar el controlador de éxito inmediatamente después del controlador de errores. Desea que sean dos rutas de código separadas. - Minor naming nitpick: estás llamando a la respuesta "
req
". Probablemente debería serres
.
Parece que tienes un modelo mental incorrecto de cómo funciona Redux Thunk, y estás tratando de combinar partes de diferentes ejemplos juntos hasta que hace clic. La sangría al azar también contribuye a que este código sea un poco difícil de entender.
Esto va a ser doloroso en el futuro, así que en su lugar, sugiero que obtenga un modelo mental más completo de lo que hace Redux Thunk, lo que return dispatch => ...
means, y cómo las promesas encajan en la imagen. Recomendaría esta respuesta como una introducción en profundidad a Redux Thunk .
Si solucionamos esos problemas, su código debería verse más o menos así:
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''})
}
);
}
}