redux-thunk-error-handler
Version:
An error handler for thunks that, when used in conjunction with `redux-thunk-recursion-detect` can handle errors thrown in both async and sync thunks gracefully.
83 lines (63 loc) • 1.91 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var reduxThunkRecursionDetect = require('redux-thunk-recursion-detect');
const handleErrorsSymbol = Symbol('handleErrors');
function createThunkErrorCatchMiddleware({
onError = logError
}) {
return ({
dispatch
}) => {
return next => action => {
let act = action;
if (shouldHandleError(action)) {
act = wrapErrorHandling(action);
}
return next(act);
};
function wrapErrorHandling(fn) {
return (...args) => {
let result;
try {
result = fn(...args);
} catch (err) {
// sync error in reducer within a thunk
errorHandler(err);
}
if (result instanceof Promise) {
// async error in thunk
return result.then(value => value, e => errorHandler(e));
}
return result;
};
}
function errorHandler(err) {
const result = onError(err);
if (typeof result === 'function') {
// no recursive error handler calls;
result[handleErrorsSymbol] = false;
return dispatch(result);
}
return result;
}
};
}
function shouldHandleError(action) {
if (typeof action !== 'function') {
return false;
}
if (typeof action[handleErrorsSymbol] === 'boolean') {
return action[handleErrorsSymbol];
}
return action[reduxThunkRecursionDetect.isNestedThunkSymbol] !== true;
}
/* This is useful for errors that happen in a nested thunk that is not awaited
by the caller */
function forceHandleError(thunkFn) {
thunkFn[handleErrorsSymbol] = true;
return thunkFn;
}
const logError = err => console.error('Unhandled error', err); // eslint-disable-line no-console
exports.default = createThunkErrorCatchMiddleware;
exports.forceHandleError = forceHandleError;
exports.handleErrorsSymbol = handleErrorsSymbol;