Imagine a situation of handling form submit which can return different errors: 400, 401, 500. When 400 is returned, I want to show a message in top of the form (override the default behavior). For other (unhandled) error codes, the default (global) error handler should be invoked (which shows notification toast). Just don't want to duplicate this code for every single action
I dispatch async actions using redux-thunk middleware
// Pseudo code
const action = (dispatch) => {
const onSuccess = (result) => dispatch({type: 'OPERATION_SUCCESS', payload: result});
const onError = (error) => dispatch({type: 'OPERATION_ERROR', error: true, payload: error});
return promise.then(onSuccess, onError);
};
dispatch(action);
I can create a reducer which handles all {error: true} actions and show some popup-notification (probably without using redux state, directly invoking some toast.show() method) But how to determine if this special error was already handled by some other reducer?
By the time an action reaches a reducer, it is a fact. It reflects something that has already happened. There is no sense in asking “has other reducer handled this action?” because reducers are supposed to be passive and, in general sense, unaware of each other’s existence. They should strive to be independent, where possible.
There is no one “true” way to accomplish what you wanted, but since you already use the convention of treating any object with an error
property as a global error, you might as well introduce another convention like “if the action has a suppressGlobalErrorNotification
flag then the global error reducer should not care about it”.
// utilities
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response
} else {
const error = new Error(response.statusText)
error.response = response
throw error
}
}
function parseJSON(response) {
return response.json()
}
export function post(url, data) {
const options = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
}
// action creators
import { post } from './utils'
export function submitForm(data) {
return dispatch => post('/myform', data).then(
response => dispatch({
type: 'SUBMIT_FORM_SUCCESS',
payload: response
}),
error => dispatch({
type: 'SUBMIT_FORM_FAILURE',
error: error,
suppressGlobalErrorNotification: (
error.response &&
error.response.status === 400
)
})
)
}
// reducers
export function error(state = null, action) {
if (!action.error || action.suppressGlobalErrorNotification) {
return state
}
if (action.type === 'RESET_ERROR') {
return null
}
return action.error
}
export function form(state = {}, action) {
switch (action.type) {
case 'SUBMIT_FORM_FAILURE':
return Object.assign({}, state, { isFormError: true })
// ...
default:
return state
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With