typedux
Version:
Slightly adjusted Redux (awesome by default) for TS
383 lines • 17.8 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spread = (this && this.__spread) || function () {
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
return ar;
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "immutable", "@3fv/logger-proxy", "../util", "shallowequal", "lodash/get", "lodash/clone", "../constants", "@3fv/prelude-ts", "../dev", "@3fv/guard", "../actions"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RootReducer = void 0;
var Immutable = __importStar(require("immutable"));
var logger_proxy_1 = require("@3fv/logger-proxy");
var util_1 = require("../util");
// import {getGlobalStateProvider} from '../actions/Actions'
var shallowequal_1 = __importDefault(require("shallowequal"));
var get_1 = __importDefault(require("lodash/get"));
var clone_1 = __importDefault(require("lodash/clone"));
var constants_1 = require("../constants");
var prelude_ts_1 = require("@3fv/prelude-ts");
var dev_1 = require("../dev");
var guard_1 = require("@3fv/guard");
var actions_1 = require("../actions");
var ActionIdCacheMax = 500, log = logger_proxy_1.getLogger(__filename);
/**
* Get leaf value
*}
* @param rootValue
* @param leaf
* @return {any}
*/
function getLeafValue(rootValue, leaf) {
if (Immutable.Map.isMap(rootValue)) {
return rootValue.get(leaf);
}
else {
return get_1.default(rootValue, leaf);
}
}
/**
* RootReducer for typedux apps
*
* Maps leaf reducers and decorated reducers
* to the appropriate state functions
*/
var RootReducer = /** @class */ (function () {
/**
* Create reducer
*
* @param rootStateType - type of root state, must be immutable map or record
* @param reducers - list of all child reducers
* @param store
*/
function RootReducer(store, rootStateType) {
var _this = this;
if (rootStateType === void 0) { rootStateType = null; }
var reducers = [];
for (var _i = 2; _i < arguments.length; _i++) {
reducers[_i - 2] = arguments[_i];
}
this.store = store;
this.rootStateType = rootStateType;
// Internal list of all leaf reducers
this.reducers = [];
// handled actions ids to avoid duplication
this.handledActionIds = [];
var leafs = [];
reducers
.filter(function (reducer) { return util_1.isFunction(reducer.leaf); })
.forEach(function (reducer) {
var leaf = reducer.leaf();
if (leafs.includes(leaf)) {
return;
}
leafs.push(leaf);
_this.reducers.push(reducer);
});
reducers
.filter(function (reducer) { return !util_1.isFunction(reducer.leaf); })
.forEach(function (reducer) { return _this.reducers.push(reducer); });
}
RootReducer.prototype.setOnError = function (onError) {
this.onError = onError;
return this;
};
/**
* Create default state
*
* @param defaultStateValue - if provided then its used as base for inflation
* @returns {State}
*/
RootReducer.prototype.defaultState = function (defaultStateValue) {
if (defaultStateValue === void 0) { defaultStateValue = null; }
// LOAD THE STATE AND VERIFY IT IS Immutable.Map/Record
var state = defaultStateValue || { type: "ROOT" };
// ITERATE REDUCERS & CREATE LEAF STATES
this.reducers
.filter(function (reducer) { return util_1.isFunction(reducer.leaf); })
.forEach(function (reducer) {
var _a;
var leaf = reducer.leaf(), leafDefaultState = getLeafValue(defaultStateValue, leaf);
state = __assign(__assign({}, state), (_a = {}, _a[leaf] = reducer.defaultState(leafDefaultState || {}), _a));
});
return state;
};
/**
* Create a generic handler for dispatches
*
* @returns {(state:S, action:ReduxAction)=>S}
*/
RootReducer.prototype.makeGenericHandler = function () {
var _this = this;
return function (state, action) {
return _this.handle(state, action);
};
};
/**
* Handle action message
*
* @param state
* @param action
* @returns {State}
*/
RootReducer.prototype.handle = function (state, action) {
var e_1, _a;
var _b, _c;
// Check if action has already been processed
if (action.id && this.handledActionIds.includes(action.id)) {
if (typeof console !== "undefined" && console.trace) {
console.trace("Duplicate action received: " + action.leaf + "/" + action.type + ", " + action.id, action);
}
return state;
}
// Push action id to the handled list
else if (action.id) {
this.handledActionIds.unshift(action.id);
if (this.handledActionIds.length > ActionIdCacheMax) {
this.handledActionIds.length = ActionIdCacheMax;
}
}
try {
/**
* Tracks whether the overall state has changed
*/
var hasChanged_1 = false;
// Guard state type as immutable
if (!state) {
state = this.defaultState(state);
hasChanged_1 = true;
}
/**
* Create a change detector func that evaluates
* a leaf for changes and updates (and sets changed flags)
* as need for tracking
*
* @param leaf
* @param currentState
* @param updateState
* @param changed
*/
var createChangeDetector_1 = function (leaf, currentState, updateState, changed) { return function (newReducerState) {
if (!newReducerState) {
throw new Error("New reducer state is null for leaf " + leaf);
}
var noMatch = !shallowequal_1.default(currentState, newReducerState);
if (noMatch) {
changed.set();
updateState(clone_1.default(newReducerState));
}
}; };
// Store a ref to the original state object
var stateMap = state;
// Hold the interim state in `tempState`
var tempState_1 = __assign({}, stateMap);
// Find the action registration
var actionReg_1 = (_c = (_b = this.store) === null || _b === void 0 ? void 0 : _b.actionContainer) === null || _c === void 0 ? void 0 : _c.getAction(action.leaf, action.type);
// Is the reg invalid, i.e. reg not found and leaf + type set
var actionRegInvalid = !actionReg_1 && [action.leaf, action.type].every(guard_1.isString);
if (dev_1.isDev && log.isDebugEnabled() && actionRegInvalid) {
log.warn("Unable to find action registration for: " + actions_1.createLeafActionType(action.leaf, action.type), action);
}
if (util_1.isFunction(actionReg_1 === null || actionReg_1 === void 0 ? void 0 : actionReg_1.action)) {
prelude_ts_1.Option.ofNullable(tempState_1[action.leaf]).ifSome(function (reducerState) {
var _a;
var leaf = action.leaf, changed = new util_1.Flag(), checkReducerStateChange = createChangeDetector_1(leaf, reducerState, function (newState) {
var _a;
tempState_1 = __assign(__assign({}, tempState_1), (_a = {}, _a[leaf] = newState, _a));
hasChanged_1 = true;
}, changed);
// ActionMessage.reducers PROVIDED
if (log.isDebugEnabled() && dev_1.isDev) {
log.debug("Action type supported", action.leaf, action.type);
}
if (action.stateType && reducerState instanceof action.stateType) {
get_1.default(action, "reducers", []).forEach(function (actionReducer) {
return checkReducerStateChange(actionReducer(reducerState, action));
});
}
// IF @ActionReducer REGISTERED
if (((_a = actionReg_1 === null || actionReg_1 === void 0 ? void 0 : actionReg_1.options) === null || _a === void 0 ? void 0 : _a.isReducer) === true) {
prelude_ts_1.Option.ofNullable(actionReg_1.action.apply(actionReg_1, __spread([null], action.args)))
.filter(util_1.isFunction)
.match({
None: function () {
throw new Error("Action reducer did not return a function: " + actionReg_1.type);
},
Some: function (reducerFn) {
if (log.isDebugEnabled() && dev_1.isDev) {
log.debug("Calling action reducer: " + actionReg_1.fullName);
}
checkReducerStateChange(reducerFn(reducerState, tempState_1));
}
});
}
});
}
var _loop_1 = function (reducer) {
var _a;
if (util_1.isFunction(reducer)) {
var simpleReducer = reducer;
var simpleState = simpleReducer(tempState_1, action);
if (simpleState !== tempState_1) {
tempState_1 = simpleState;
hasChanged_1 = true;
}
return "continue";
}
var // Get the reducer leaf
leaf = reducer.leaf(),
// Get Current RAW state
rawLeafState = tempState_1[leaf],
// Shape it for the reducer
startReducerState = rawLeafState, stateChangeDetected = new util_1.Flag();
var reducerState = startReducerState;
try {
/**
* Check the returned state from every handler for changes
*
* @param newReducerState
*/
var checkReducerStateChange = createChangeDetector_1(leaf, reducerState, function (newState) {
reducerState = newState;
}, stateChangeDetected);
// Check internal actions
if (constants_1.INTERNAL_ACTIONS.includes(action.type)) {
if (log.isDebugEnabled() && dev_1.isDev) {
log.debug("Sending init event to " + leaf + " - internal action received " + action.type);
}
if (constants_1.INTERNAL_ACTION.INIT === action.type && reducer.init) {
checkReducerStateChange(reducer.init(startReducerState));
}
}
// Check leaf of reducer and action to see if this reducer handles the supplied action
if (action.leaf && action.leaf !== leaf) {
return "continue";
}
// CHECK REDUCER.HANDLE
if (reducer.handle) {
checkReducerStateChange(reducer.handle(reducerState, action));
}
// CHECK ACTUAL REDUCER FOR SUPPORT
if (util_1.isFunction(reducer[action.type])) {
checkReducerStateChange(reducer[action.type].apply(reducer, __spread([reducerState], action.args)));
}
}
catch (err) {
log.error("Error occurred on reducer leaf " + leaf, err);
if (reducer.handleError) {
reducer.handleError(startReducerState, action, err);
}
this_1.onError && this_1.onError(err, reducer);
}
if (stateChangeDetected) {
tempState_1 = __assign(__assign({}, tempState_1), (_a = {}, _a[leaf] = reducerState, _a));
hasChanged_1 = true;
}
};
var this_1 = this;
try {
// Iterate leaves and execute actions
for (var _d = __values(this.reducers), _e = _d.next(); !_e.done; _e = _d.next()) {
var reducer = _e.value;
_loop_1(reducer);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
}
finally { if (e_1) throw e_1.error; }
}
if (log.isDebugEnabled() && dev_1.isDev) {
log.debug("Has changed after all reducers", hasChanged_1, "states equal", shallowequal_1.default(tempState_1, state));
}
return (hasChanged_1 ? tempState_1 : state);
}
catch (err) {
log.error("Error bubbled to root reducer", err);
// If error handler exists then use it
if (this.onError) {
this.onError && this.onError(err);
return state;
}
// Otherwise throw
throw err;
}
};
return RootReducer;
}());
exports.RootReducer = RootReducer;
// Export the RootReducer class as the default
exports.default = RootReducer;
});
// export default (state:any,action:any):any => {
// return rootReducer.handle(state as DefaultStateType, action as ActionMessage<any>)
// }
//# sourceMappingURL=RootReducer.js.map