- Action creators must return something "simple". A plain object, a simple function call, or at worst: a thunk.
- Internal API call logic must be encapsulated and abstracted away out of sight
- authorization token decoration
- refresh and retry logic
- logout on fail logic
- External API calls are subject to the same async processing as internal API calls (pending, success, fail events at minimum)
- Internal API results trigger notifications for success and fail, according to a limited set of rules
- Calls can be correctly chained (i.e. failures exit the chain), with access to previous call's response
// regular single call action
function updateUser(username, data) {
const request = { url: `/users/${username}`, method: 'PUT', body: data };
return {
type: 'UPDATE_USER',
async: (dispatch, getState) => helper({ request, dispatch, getState }),
meta: {
notify: {
success: (result) => `${username} was successfully updated.`,
fail: (err) => `There was a problem trying to update `${username}`
},
payloads: {
success: () => ({ username })
}
}
};
}
// advanced chained call action
function updateBilling(data) {
return {
type: 'UPDATE_BILLING',
async: (dispatch, getState) => {
return helper({ request: corsRequest, dispatch, getState })
.then(({ signature }) => billingHelper({ request: billingRequest, headers: { signature }))
.then(() => helper({ request: syncAccountRequest, dispatch, getState }))
.then(() => helper({ request: getAccountRequest, dispatch, getState }));
},
meta: {
notify: {
success: () => 'Billing successfully updated.',
fail: () => 'Problem updating billing.'
},
payloads: {
success: (response) => ({ data })
}
}
}
};
// the async dispatcher middleware listens for actions with action.async
if (action.async) {
dispatch({ type: types.PENDING });
action.async(dispatch, getState)
.then((response) => {
dispatch({ type: types.SUCCESS, payload: { response, ...meta.payloads.success(response) });
meta.notify.success && dispatch(globalAlert({ type: 'success', message: meta.notify.success(response) }));
})
.catch((err) => {
dispatch({ type: types.FAIL, payload: err });
meta.notify.fail && dispatch(globalAlert({ type: 'error', message: message.notify.error(err) }));
});
}