I\'m making API requests using isomorphic fetch, and using Redux to handle my app\'s state.
I want to handle both internet connection loss errors, and API errors, by
I’m confused about several things:
Promise.all
around dispatching two synchronous actions? Calling dispatch
with something like {type: PRE_FETCH_RESOURCES_FAIL, ...}
won’t return a Promise, so Promise.all
is unnecessary. Promise.all()
is only useful if the actions you dispatch are themselves written as thunk action creators, which is not the case here.return dispatch => ...
is only necessary once at the very beginning of the action creators. 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. This is a way to inject dispatch
into your function at the top level, and there is no point to repeating it.then
after a catch
, it will run even after an error was caught. 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.req
”. It should probably be 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. 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. I would recommend this answer as an in-depth introduction to 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)
])