While I do understand this, and get that it can make testing easier considering the pure nature vs having a mutating function based on the response of fetch (for example), I still find using redux-thunk with async action creators to be easier to comprehend and lower complexity in terms of code.
const loadTodosStart = () => {
type: 'LOAD_TODO_START',
};
const loadTodosSuccess = payload => {
type: 'LOAD_TODO_SUCCESS',
payload,
};
const loadTodosError = payload => {
type: 'LOAD_TODO_ERROR',
payload,
};
const loadTodos = () => async (dispatch, getState) => {
dispatch(loadTodosStart());
try {
dispatch(loadTodosSuccess(await fetch('/todos')));
} catch(error) {
dispatch(loadTodosError(error);
}
};
Though, I wouldn't mind something similar that I could use as follows...
// library
const handleActionType = (type, payload) => (
typeof type === 'function'
? type(payload)
: { type, payload }
);
export const createAction =
(beforeType, successType, errorType, fn) => (...args) => (dispatch) => {
dispatch(handleActionType(beforeType, args))
return Promise.resolve(fn(...args))
.then(payload => dispatch(handleActionType(successType, payload)))
.catch(payload => dispatch(handleActionType(errorType, payload)))
};
// actions.js
export loadTodos = createAction(
'LOAD_TODO_START', // before
'LOAD_TODO_SUCCESS', // then payload = result
'LOAD_TODO_ERROR', // catch payload = error
async (/* exposed args here */) => fetch('/todos'),
);
Where the final method is the signature createAction exposes, wraps in try/catch and exposes as a thunk/function wrapper to be used with redux-thunk