UNPKG

redux-promise-middleware-global-action

Version:

Redux middleware for promises, async functions and conditional optimistic updates

211 lines (172 loc) 7.98 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.REJECTED = exports.FULFILLED = exports.PENDING = undefined; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; exports.default = promiseMiddleware; var _lodash = require('lodash'); var _isPromise = require('./isPromise.js'); var _isPromise2 = _interopRequireDefault(_isPromise); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // The default async action types var PENDING = exports.PENDING = 'PENDING'; var FULFILLED = exports.FULFILLED = 'FULFILLED'; var REJECTED = exports.REJECTED = 'REJECTED'; var defaultTypes = [PENDING, FULFILLED, REJECTED]; /** * Function: promiseMiddleware * Description: The main promiseMiddleware accepts a configuration * object and returns the middleware. */ function promiseMiddleware() { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var PROMISE_TYPE_SUFFIXES = config.promiseTypeSuffixes || defaultTypes; var PROMISE_TYPE_DELIMITER = config.promiseTypeDelimiter || '_'; return function (ref) { var dispatch = ref.dispatch; return function (next) { return function (action) { /** * Instantiate variables to hold: * (1) the promise * (2) the data for optimistic updates */ var promise = void 0; var data = void 0; /** * There are multiple ways to dispatch a promise. The first step is to * determine if the promise is defined: * (a) explicitly (action.payload.promise is the promise) * (b) implicitly (action.payload is the promise) * (c) as an async function (returns a promise when called) * * If the promise is not defined in one of these three ways, we don't do * anything and move on to the next middleware in the middleware chain. */ // Step 1a: Is there a payload? if (action.payload) { var PAYLOAD = action.payload; // Step 1.1: Is the promise implicitly defined? if ((0, _isPromise2.default)(PAYLOAD)) { promise = PAYLOAD; } // Step 1.2: Is the promise explicitly defined? else if ((0, _isPromise2.default)(PAYLOAD.promise)) { promise = PAYLOAD.promise; data = PAYLOAD.data; } // Step 1.3: Is the promise returned by an async function? else if (typeof PAYLOAD === 'function' || typeof PAYLOAD.promise === 'function') { promise = PAYLOAD.promise ? PAYLOAD.promise() : PAYLOAD(); data = PAYLOAD.promise ? PAYLOAD.data : undefined; // Step 1.3.1: Is the return of action.payload a promise? if (!(0, _isPromise2.default)(promise)) { // If not, move on to the next middleware. return next(_extends({}, action, { payload: promise })); } } // Step 1.4: If there's no promise, move on to the next middleware. else { return next(action); } // Step 1b: If there's no payload, move on to the next middleware. } else { return next(action); } /** * Instantiate and define constants for: * (1) the action type * (2) the action meta */ var TYPE = action.type; var META = action.meta; var otherAction = (0, _lodash.omit)(action, ['type', 'meta', 'payload']); /** * Instantiate and define constants for the action type suffixes. * These are appended to the end of the action type. */ var _PROMISE_TYPE_SUFFIXE = _slicedToArray(PROMISE_TYPE_SUFFIXES, 3), _PENDING = _PROMISE_TYPE_SUFFIXE[0], _FULFILLED = _PROMISE_TYPE_SUFFIXE[1], _REJECTED = _PROMISE_TYPE_SUFFIXE[2]; /** * Function: getAction * Description: This function constructs and returns a rejected * or fulfilled action object. The action object is based off the Flux * Standard Action (FSA). * * Given an original action with the type FOO: * * The rejected object model will be: * { * error: true, * type: 'FOO_REJECTED', * payload: ..., * meta: ... (optional) * } * * The fulfilled object model will be: * { * type: 'FOO_FULFILLED', * payload: ..., * meta: ... (optional) * } */ var getAction = function getAction(newPayload, isRejected) { return _extends({ // Concatentate the type string property. type: [TYPE, isRejected ? _REJECTED : _FULFILLED].join(PROMISE_TYPE_DELIMITER) }, newPayload === null || typeof newPayload === 'undefined' ? {} : { payload: newPayload }, META !== undefined ? { meta: META } : {}, isRejected ? { error: true } : {}, otherAction); }; /** * Function: handleReject * Calls: getAction to construct the rejected action * Description: This function dispatches the rejected action and returns * the original Error object. Please note the developer is responsible * for constructing and throwing an Error object. The middleware does not * construct any Errors. */ var handleReject = function handleReject(reason) { var rejectedAction = getAction(reason, true); dispatch(rejectedAction); throw reason; }; /** * Function: handleFulfill * Calls: getAction to construct the fullfilled action * Description: This function dispatches the fulfilled action and * returns the success object. The success object should * contain the value and the dispatched action. */ var handleFulfill = function handleFulfill() { var value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var resolvedAction = getAction(value, false); dispatch(resolvedAction); return { value: value, action: resolvedAction }; }; /** * First, dispatch the pending action: * This object describes the pending state of a promise and will include * any data (for optimistic updates) and/or meta from the original action. */ next(_extends({ // Concatentate the type string. type: [TYPE, _PENDING].join(PROMISE_TYPE_DELIMITER) }, data !== undefined ? { payload: data } : {}, META !== undefined ? { meta: META } : {})); /** * Second, dispatch a rejected or fulfilled action and move on to the * next middleware. */ return promise.then(handleFulfill, handleReject); }; }; }; }