UNPKG

react-redux-dispatch-async

Version:

hooks & redux middleware to be able to wait async actions with fixed defined suffixes

230 lines (197 loc) 6.21 kB
'use strict'; var react = require('react'); var reactRedux = require('react-redux'); var createId = function createId() { return '_' + Math.random().toString(36).substr(2, 9); }; var listeners = {}; var addActionListener = function addActionListener(newListener) { var listenerId = createId(); listeners[listenerId] = newListener; return function () { return delete listeners[listenerId]; }; }; var ConfigMiddleware = { initialized: false, suffixes: { request: 'REQUESTED', success: 'SUCCEEDED', failure: 'FAILED', cancel: 'CANCELED' } }; var isAsyncAction = function isAsyncAction(config, action) { return action.type.endsWith(config.request) || action.type.endsWith(config.success) || action.type.endsWith(config.failure) || action.type.endsWith(config.cancel); }; var createDispatchAsyncMiddleware = function createDispatchAsyncMiddleware(config) { return function () { return function (next) { return function (action) { try { if (config && !ConfigMiddleware.initialized) { ConfigMiddleware.suffixes = config; ConfigMiddleware.initialized = true; } if (isAsyncAction(ConfigMiddleware.suffixes, action) && Object.keys(listeners).length > 0) { for (var _i = 0, _Object$values = Object.values(listeners); _i < _Object$values.length; _i++) { var listener = _Object$values[_i]; listener(action); } } } catch (error) { console.error(error); } return next(action); }; }; }; }; function dispatchAsync(dispatch, action) { return new Promise(function (resolve) { var actionNameBase = action.type.replace("_" + ConfigMiddleware.suffixes.request, ''); var unsubscribe = addActionListener(function (resultAction) { if (resultAction.type === actionNameBase + "_" + ConfigMiddleware.suffixes.success) { resolve({ success: true, result: resultAction.payload }); unsubscribe(); } else if (resultAction.type === actionNameBase + "_" + ConfigMiddleware.suffixes.failure) { var error = resultAction.payload instanceof Error ? resultAction.payload : new Error("Action failure: " + actionNameBase); resolve({ success: false, error: error }); unsubscribe(); } else if (resultAction.type === actionNameBase + "_" + ConfigMiddleware.suffixes.cancel) { resolve({ success: false, error: new Error('canceled'), canceled: true }); unsubscribe(); } }); dispatch(action); }); } function useCompatDispatchAsync(action) { var dispatch = reactRedux.useDispatch(); if (action) { return function () { return dispatchAsync(dispatch, action); }; } return function (action) { return dispatchAsync(dispatch, action); }; } // the hook function useDispatchAsync(actionFunction, deps, options) { if (deps === void 0) { deps = []; } if (options === void 0) { options = { timeoutInMilliseconds: 15000 }; } var dispatch = reactRedux.useDispatch(); // 👉 Better flow with informative & useful return var _useState = react.useState(undefined), result = _useState[0], setResult = _useState[1]; var _useState2 = react.useState(undefined), error = _useState2[0], setError = _useState2[1]; var _useState3 = react.useState(false), isTimeout = _useState3[0], setIsTimeout = _useState3[1]; var _useState4 = react.useState(false), isCancel = _useState4[0], setIsCancel = _useState4[1]; // 👉 race condition to get last update // https://sebastienlorber.com/handling-api-request-race-conditions-in-react // A ref to store the last issued pending request var lastPromise = react.useRef(); react.useEffect(function () { var actionPromise = dispatchAsync(dispatch, actionFunction.apply(void 0, deps)); var timeoutPromise = new Promise(function (resolve) { var _options; return setTimeout(function () { return resolve(false); }, (_options = options) === null || _options === void 0 ? void 0 : _options.timeoutInMilliseconds); }); var currentPromise = Promise.race([actionPromise, timeoutPromise]); lastPromise.current = currentPromise; currentPromise.then(function (res) { // filtering last update promise if (currentPromise === lastPromise.current) { // filtering timeout if (typeof res !== 'boolean') { // filtering success if (res.success) { setResult(res.result); } else { if (!res.canceled) { setError(res.error); } else { setIsCancel(true); } } } else { setIsTimeout(true); } } })["catch"](function (e) { if (currentPromise === lastPromise.current) { console.error('useDispatchAsync: Unexpected error', e); setError(e); } }); }, deps); var status = react.useMemo(function () { if (!result && !error && !isTimeout && !isCancel) { return 'loading'; } if (result) { return 'success'; } if (isCancel) { return 'canceled'; } if (error) { return 'error'; } if (isTimeout) { return 'timeout'; } return 'unknown'; }, [result, error, isTimeout, isCancel]); switch (status) { case 'loading': case 'timeout': case 'canceled': return { status: status }; case 'success': return { result: result, status: status }; case 'error': return { error: error, status: status }; default: return { status: 'unknown' }; } } exports.ConfigMiddleware = ConfigMiddleware; exports.createDispatchAsyncMiddleware = createDispatchAsyncMiddleware; exports.dispatchAsync = dispatchAsync; exports.useCompatDispatchAsync = useCompatDispatchAsync; exports.useDispatchAsync = useDispatchAsync; //# sourceMappingURL=react-redux-dispatch-async.cjs.development.js.map