UNPKG

@shoutem/redux-composers

Version:

Reducers you can use for composing reducer hierarchy like with combineReducers from redux

193 lines (149 loc) 7.37 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TARGET_ALL_REDUCERS = exports.TARGET_ALL_OPTION_KEY = void 0; exports.applyToAll = applyToAll; exports["default"] = mapReducers; exports.isTargetAllAction = isTargetAllAction; exports.isTargetAllKey = isTargetAllKey; exports.mapReducer = mapReducer; exports.mapReducerFactory = mapReducerFactory; var _identity2 = _interopRequireDefault(require("lodash/identity")); var _mapValues2 = _interopRequireDefault(require("lodash/mapValues")); var _get2 = _interopRequireDefault(require("lodash/get")); var _isEmpty2 = _interopRequireDefault(require("lodash/isEmpty")); var _isString2 = _interopRequireDefault(require("lodash/isString")); var _isFunction2 = _interopRequireDefault(require("lodash/isFunction")); var _shallowequal = _interopRequireDefault(require("shallowequal")); var _actionOptions = require("./actionOptions"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } // Action marked as TARGET_ALL_OPTION_KEY bypass key:reducer relationship at map reducers // and dispatch action to all mapped reducers. var TARGET_ALL_OPTION_KEY = 'targetAll'; // If key selector returns TARGET_ALL_REDUCERS value, all mapped reducers will be activated exports.TARGET_ALL_OPTION_KEY = TARGET_ALL_OPTION_KEY; var TARGET_ALL_REDUCERS = 'TARGET_ALL'; exports.TARGET_ALL_REDUCERS = TARGET_ALL_REDUCERS; function validateKeySelector(keySelector) { if (!(0, _isFunction2["default"])(keySelector)) { if (!(0, _isString2["default"])(keySelector)) { throw new Error('KeySelector argument should be a function or a string'); } if ((0, _isEmpty2["default"])(keySelector)) { throw new Error('KeySelector argument is empty string.'); } } } function resolveKey(action, keySelector) { var resolvedKey = (0, _isFunction2["default"])(keySelector) ? keySelector(action) : (0, _get2["default"])(action, keySelector); return resolvedKey || TARGET_ALL_REDUCERS; } /** * Calculates new state for all keys applying reducer resolved based on key, * if state is not modified returns same state instance * @param state - redux state * @param action - redux action * @param resolveReducer - returns reducer based on state key * @returns {*} state - new or identical instance based on changes */ function calculateNewStateForAllKeys(state, action, resolveReducer) { var newState = (0, _mapValues2["default"])(state, function (stateForKey, key) { var reducer = resolveReducer(key) || _identity2["default"]; return reducer(stateForKey, action); }); if ((0, _shallowequal["default"])(state, newState)) { return state; } return newState; } /** * Calculates new state applying reducer on substate * based on provided key if state is not modified * returns same state instance * @param state - redux state * @param action - redux action * @param key - select substate to apply reducer * @param reducer - redux reducer function * @returns {*} state - new or identical instance based on changes */ function calculateNewStateForSingleKey(state, action, key, reducer) { var stateForKey = state[key]; var newStateForKey = reducer(stateForKey, action); if (stateForKey === newStateForKey) { return state; } return _objectSpread(_objectSpread({}, state), {}, _defineProperty({}, key, newStateForKey)); } function isTargetAllAction(action) { var actionOptions = (0, _actionOptions.getActionOptions)(action); return (0, _get2["default"])(actionOptions, TARGET_ALL_OPTION_KEY); } function applyToAll(action) { return (0, _actionOptions.setActionOption)(action, TARGET_ALL_OPTION_KEY, true); } function isTargetAllKey(keyValue) { return keyValue === TARGET_ALL_REDUCERS; } function mapReducer(keySelector, reducer) { validateKeySelector(keySelector); return function () { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var action = arguments.length > 1 ? arguments[1] : undefined; var key = resolveKey(action, keySelector); if (!key) { return state; } var targetAllKey = isTargetAllKey(key); var targetAllAction = isTargetAllAction(action); if (targetAllAction || targetAllKey) { return calculateNewStateForAllKeys(state, action, function () { return reducer; }); } return calculateNewStateForSingleKey(state, action, key, reducer); }; } function mapReducerFactory(keySelector, reducerFactory) { var reducers = {}; validateKeySelector(keySelector); return function () { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var action = arguments.length > 1 ? arguments[1] : undefined; var key = resolveKey(action, keySelector); if (!key) { return state; } var targetAllKey = isTargetAllKey(key); var targetAllAction = isTargetAllAction(action); if (targetAllAction || targetAllKey) { return calculateNewStateForAllKeys(state, action, function (stateKey) { return reducers[stateKey] || reducerFactory(stateKey); }); } reducers[key] = reducers[key] || reducerFactory(key); var reducer = reducers[key]; return calculateNewStateForSingleKey(state, action, key, reducer); }; } /** * MapReducers is composer that enables applying reducer on substate that is selected based * on key. Key is selected from action based on keySelector. It allows for one reducer to be * applied to substate depending on key in action or to dynamically create reducer for each * key and apply it on substate. * @param keySelector is function that returns key or path to key in action * @param reducer can be normal redux reducer or reducer factory. In either way defined or * created reducer is applied to part of state under key defined in action. Reducer factory * is function that receives key and returns reducer function. In that way enabling to create * dynamic reducers for different keys. * @returns {Function} */ function mapReducers(keySelector, reducer) { var result = reducer(undefined, {}); if ((0, _isFunction2["default"])(result)) { return mapReducerFactory(keySelector, reducer); } return mapReducer(keySelector, reducer); }