diff --git a/README.md b/README.md index f349e46..184691b 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ * [Create store](./doc/CreateStore.md) * [Reducers](./doc/Reducers.md) * [Middlewares](./doc/Middlewares.md) + * [Sections](./doc/Sections.md) * [Debugging](./doc/Debugging.md) * [Use it in React](./doc/React.md) * [Examples](./doc/Examples.md) @@ -276,6 +277,7 @@ Learn the api to master the Dynadux. - [Create store](./doc/CreateStore.md) - [Reducers](./doc/Reducers.md) - [Middlewares](./doc/Middlewares.md) +- [Sections](./doc/Sections.md) - [Debugging](./doc/Debugging.md) - 🎉 and that's it, you are mastering the Dynadux! diff --git a/dist/commonJs/Dynadux/Dynadux.d.ts b/dist/commonJs/Dynadux/Dynadux.d.ts index 68fc3f8..e343039 100644 --- a/dist/commonJs/Dynadux/Dynadux.d.ts +++ b/dist/commonJs/Dynadux/Dynadux.d.ts @@ -1,6 +1,6 @@ export interface IDynaduxConfig { initialState?: TState; - reducers: IDynaduxReducerDic | IDynaduxReducerDic[]; + reducers?: IDynaduxReducerDic | IDynaduxReducerDic[]; middlewares?: IDynaduxMiddleware[]; onDispatch?: (action: string, payload: any) => void; onChange?: (state: TState) => void; @@ -40,9 +40,11 @@ export declare class Dynadux { private _state; private readonly _dispatches; private _isDispatching; - private readonly _reducers; + private _reducers; constructor(_config: IDynaduxConfig); get state(): TState; + setSectionInitialState(section: string, sectionState: any): void; + addReducers: (reducers: IDynaduxReducerDic) => void; dispatch: (action: string, payload: TPayload) => void; private _dispatch; } diff --git a/dist/commonJs/Dynadux/Dynadux.js b/dist/commonJs/Dynadux/Dynadux.js index 71edc22..6750a6c 100644 --- a/dist/commonJs/Dynadux/Dynadux.js +++ b/dist/commonJs/Dynadux/Dynadux.js @@ -18,6 +18,9 @@ var Dynadux = /** @class */ (function () { this._config = _config; this._dispatches = []; this._isDispatching = false; + this.addReducers = function (reducers) { + _this._reducers = combineMultipleReducers_1.combineMultipleReducers(_this._reducers, reducers); + }; this.dispatch = function (action, payload) { _this._dispatches.push({ action: action, payload: payload }); _this._dispatch(); @@ -77,11 +80,11 @@ var Dynadux = /** @class */ (function () { _this._isDispatching = false; _this._dispatch(); }; - var _a = this._config, _b = _a.initialState, initialState = _b === void 0 ? {} : _b, _c = _a.middlewares, middlewares = _c === void 0 ? [] : _c; + var _a = this._config, _b = _a.initialState, initialState = _b === void 0 ? {} : _b, _c = _a.reducers, reducers = _c === void 0 ? {} : _c, _d = _a.middlewares, middlewares = _d === void 0 ? [] : _d; this._state = initialState; this._reducers = - Array.isArray(this._config.reducers) - ? combineMultipleReducers_1.combineMultipleReducers.apply(void 0, this._config.reducers) : this._config.reducers; + Array.isArray(reducers) + ? combineMultipleReducers_1.combineMultipleReducers.apply(void 0, reducers) : reducers; middlewares.forEach(function (middleware) { return middleware.init && middleware.init(_this); }); } Object.defineProperty(Dynadux.prototype, "state", { @@ -91,6 +94,9 @@ var Dynadux = /** @class */ (function () { enumerable: true, configurable: true }); + Dynadux.prototype.setSectionInitialState = function (section, sectionState) { + this._state[section] = sectionState; + }; return Dynadux; }()); exports.Dynadux = Dynadux; diff --git a/dist/commonJs/Dynadux/Dynadux.js.map b/dist/commonJs/Dynadux/Dynadux.js.map index 71a0883..fce1b0e 100644 --- a/dist/commonJs/Dynadux/Dynadux.js.map +++ b/dist/commonJs/Dynadux/Dynadux.js.map @@ -1 +1 @@ -{"version":3,"file":"Dynadux.js","sourceRoot":"","sources":["../../../src/Dynadux/Dynadux.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Dynadux.js","sourceRoot":"","sources":["../../../src/Dynadux/Dynadux.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/commonJs/createStore/convertReducersToSectionReducers.d.ts b/dist/commonJs/createStore/convertReducersToSectionReducers.d.ts new file mode 100644 index 0000000..661db67 --- /dev/null +++ b/dist/commonJs/createStore/convertReducersToSectionReducers.d.ts @@ -0,0 +1,2 @@ +import { IDynaduxReducerDic } from "../Dynadux/Dynadux"; +export declare const convertReducersToSectionReducers: (section: string, sectionReducers: IDynaduxReducerDic) => IDynaduxReducerDic; diff --git a/dist/commonJs/createStore/convertReducersToSectionReducers.js b/dist/commonJs/createStore/convertReducersToSectionReducers.js new file mode 100644 index 0000000..56fec06 --- /dev/null +++ b/dist/commonJs/createStore/convertReducersToSectionReducers.js @@ -0,0 +1,29 @@ +"use strict"; +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); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.convertReducersToSectionReducers = function (section, sectionReducers) { + return Object.keys(sectionReducers) + .reduce(function (acc, action) { + var originalReducer = sectionReducers[action]; + acc[action] = function (params) { + var _a; + var subPartialState = originalReducer(__assign(__assign({}, params), { state: params.state[section] })); + if (subPartialState) + return _a = {}, + _a[section] = __assign(__assign({}, params.state[section]), subPartialState), + _a; + }; + return acc; + }, {}); +}; +//# sourceMappingURL=convertReducersToSectionReducers.js.map \ No newline at end of file diff --git a/dist/commonJs/createStore/convertReducersToSectionReducers.js.map b/dist/commonJs/createStore/convertReducersToSectionReducers.js.map new file mode 100644 index 0000000..0684bc0 --- /dev/null +++ b/dist/commonJs/createStore/convertReducersToSectionReducers.js.map @@ -0,0 +1 @@ +{"version":3,"file":"convertReducersToSectionReducers.js","sourceRoot":"","sources":["../../../src/createStore/convertReducersToSectionReducers.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAKa,QAAA,gCAAgC,GAAG,UAAC,OAAe,EAAE,eAAwC;IACxG,OAAO,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC;SAChC,MAAM,CAAC,UAAC,GAA4B,EAAE,MAAc;QACnD,IAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QAEhD,GAAG,CAAC,MAAM,CAAC,GAAG,UAAC,MAAoC;;YACjD,IAAM,eAAe,GAAG,eAAe,uBAClC,MAAM,KACT,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAC5B,CAAC;YAEH,IAAI,eAAe;gBAAE,OAAO;oBAC1B,GAAC,OAAO,0BACH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,GACrB,eAAe,CACnB;sBACK,CAAC;QACX,CAAC,CAAC;QAEF,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,EAAE,CAAC,CAAC;AACX,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/commonJs/createStore/createStore.d.ts b/dist/commonJs/createStore/createStore.d.ts index 7531b2c..9b3ef54 100644 --- a/dist/commonJs/createStore/createStore.d.ts +++ b/dist/commonJs/createStore/createStore.d.ts @@ -1,8 +1,19 @@ -import { IDynaduxConfig, TDynaduxDispatch } from "../Dynadux/Dynadux"; +import { IDynaduxConfig, TDynaduxDispatch, IDynaduxReducerDic } from "../Dynadux/Dynadux"; export interface ICreateStoreConfig extends IDynaduxConfig { } export interface ICreateStoreAPI { - dispatch: TDynaduxDispatch; state: TState; + dispatch: TDynaduxDispatch; + createSection: (createSectionConfig: ICreateSectionConfig) => ICreateSectionAPI; +} +export interface ICreateSectionConfig { + section: string; + initialState: TSectionState; + reducers: IDynaduxReducerDic; +} +export interface ICreateSectionAPI { + storeState: TState; + state: TSectionState; + dispatch: TDynaduxDispatch; } export declare const createStore: (config: ICreateStoreConfig) => ICreateStoreAPI; diff --git a/dist/commonJs/createStore/createStore.js b/dist/commonJs/createStore/createStore.js index c1332be..ed51891 100644 --- a/dist/commonJs/createStore/createStore.js +++ b/dist/commonJs/createStore/createStore.js @@ -1,11 +1,30 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var Dynadux_1 = require("../Dynadux/Dynadux"); +var convertReducersToSectionReducers_1 = require("./convertReducersToSectionReducers"); exports.createStore = function (config) { var dynadux = new Dynadux_1.Dynadux(config); return { + get state() { + return dynadux.state; + }, dispatch: dynadux.dispatch, - get state() { return dynadux.state; }, + createSection: function (createSectionConfig) { + var section = createSectionConfig.section, initialState = createSectionConfig.initialState, reducers = createSectionConfig.reducers; + if (dynadux.state[section]) + throw new Error("dynadux: createSection: Section or root property \"" + section + "\" already exists, section couldn't be created."); + dynadux.setSectionInitialState(section, initialState); + dynadux.addReducers(convertReducersToSectionReducers_1.convertReducersToSectionReducers(section, reducers)); + return { + get storeState() { + return dynadux.state; + }, + get state() { + return dynadux.state[section]; + }, + dispatch: dynadux.dispatch, + }; + } }; }; //# sourceMappingURL=createStore.js.map \ No newline at end of file diff --git a/dist/commonJs/createStore/createStore.js.map b/dist/commonJs/createStore/createStore.js.map index c96b1ca..c6d9eb2 100644 --- a/dist/commonJs/createStore/createStore.js.map +++ b/dist/commonJs/createStore/createStore.js.map @@ -1 +1 @@ -{"version":3,"file":"createStore.js","sourceRoot":"","sources":["../../../src/createStore/createStore.ts"],"names":[],"mappings":";;AAAA,8CAI4B;AAUf,QAAA,WAAW,GAAG,UAAe,MAAkC;IAC1E,IAAM,OAAO,GAAG,IAAI,iBAAO,CAAS,MAAM,CAAC,CAAC;IAE5C,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,IAAI,KAAK,KAAK,OAAQ,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;KACvC,CAAC;AACJ,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"createStore.js","sourceRoot":"","sources":["../../../src/createStore/createStore.ts"],"names":[],"mappings":";;AAAA,8CAK4B;AAC5B,uFAAsF;AAuBzE,QAAA,WAAW,GAAG,UAAe,MAAkC;IAC1E,IAAM,OAAO,GAAG,IAAI,iBAAO,CAAS,MAAM,CAAC,CAAC;IAE5C,OAAO;QACL,IAAI,KAAK;YACP,OAAO,OAAO,CAAC,KAAK,CAAC;QACvB,CAAC;QACD,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,aAAa,EAAE,UAAgB,mBAAwD;YAEnF,IAAA,qCAAO,EACP,+CAAY,EACZ,uCAAQ,CACc;YAExB,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,wDAAqD,OAAO,oDAAgD,CAAC,CAAC;YAE1J,OAAO,CAAC,sBAAsB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YACtD,OAAO,CAAC,WAAW,CAAC,mEAAgC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;YAEzE,OAAO;gBACL,IAAI,UAAU;oBACZ,OAAO,OAAO,CAAC,KAAK,CAAC;gBACvB,CAAC;gBACD,IAAI,KAAK;oBACP,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAChC,CAAC;gBACD,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC3B,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/esNext/Dynadux/Dynadux.d.ts b/dist/esNext/Dynadux/Dynadux.d.ts index 68fc3f8..e343039 100644 --- a/dist/esNext/Dynadux/Dynadux.d.ts +++ b/dist/esNext/Dynadux/Dynadux.d.ts @@ -1,6 +1,6 @@ export interface IDynaduxConfig { initialState?: TState; - reducers: IDynaduxReducerDic | IDynaduxReducerDic[]; + reducers?: IDynaduxReducerDic | IDynaduxReducerDic[]; middlewares?: IDynaduxMiddleware[]; onDispatch?: (action: string, payload: any) => void; onChange?: (state: TState) => void; @@ -40,9 +40,11 @@ export declare class Dynadux { private _state; private readonly _dispatches; private _isDispatching; - private readonly _reducers; + private _reducers; constructor(_config: IDynaduxConfig); get state(): TState; + setSectionInitialState(section: string, sectionState: any): void; + addReducers: (reducers: IDynaduxReducerDic) => void; dispatch: (action: string, payload: TPayload) => void; private _dispatch; } diff --git a/dist/esNext/Dynadux/Dynadux.js b/dist/esNext/Dynadux/Dynadux.js index ecfcae1..1742ce9 100644 --- a/dist/esNext/Dynadux/Dynadux.js +++ b/dist/esNext/Dynadux/Dynadux.js @@ -16,6 +16,9 @@ var Dynadux = /** @class */ (function () { this._config = _config; this._dispatches = []; this._isDispatching = false; + this.addReducers = function (reducers) { + _this._reducers = combineMultipleReducers(_this._reducers, reducers); + }; this.dispatch = function (action, payload) { _this._dispatches.push({ action: action, payload: payload }); _this._dispatch(); @@ -75,11 +78,11 @@ var Dynadux = /** @class */ (function () { _this._isDispatching = false; _this._dispatch(); }; - var _a = this._config, _b = _a.initialState, initialState = _b === void 0 ? {} : _b, _c = _a.middlewares, middlewares = _c === void 0 ? [] : _c; + var _a = this._config, _b = _a.initialState, initialState = _b === void 0 ? {} : _b, _c = _a.reducers, reducers = _c === void 0 ? {} : _c, _d = _a.middlewares, middlewares = _d === void 0 ? [] : _d; this._state = initialState; this._reducers = - Array.isArray(this._config.reducers) - ? combineMultipleReducers.apply(void 0, this._config.reducers) : this._config.reducers; + Array.isArray(reducers) + ? combineMultipleReducers.apply(void 0, reducers) : reducers; middlewares.forEach(function (middleware) { return middleware.init && middleware.init(_this); }); } Object.defineProperty(Dynadux.prototype, "state", { @@ -89,6 +92,9 @@ var Dynadux = /** @class */ (function () { enumerable: true, configurable: true }); + Dynadux.prototype.setSectionInitialState = function (section, sectionState) { + this._state[section] = sectionState; + }; return Dynadux; }()); export { Dynadux }; diff --git a/dist/esNext/Dynadux/Dynadux.js.map b/dist/esNext/Dynadux/Dynadux.js.map index f7b7bb2..0bacfb6 100644 --- a/dist/esNext/Dynadux/Dynadux.js.map +++ b/dist/esNext/Dynadux/Dynadux.js.map @@ -1 +1 @@ -{"version":3,"file":"Dynadux.js","sourceRoot":"","sources":["../../../src/Dynadux/Dynadux.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Dynadux.js","sourceRoot":"","sources":["../../../src/Dynadux/Dynadux.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/esNext/createStore/convertReducersToSectionReducers.d.ts b/dist/esNext/createStore/convertReducersToSectionReducers.d.ts new file mode 100644 index 0000000..661db67 --- /dev/null +++ b/dist/esNext/createStore/convertReducersToSectionReducers.d.ts @@ -0,0 +1,2 @@ +import { IDynaduxReducerDic } from "../Dynadux/Dynadux"; +export declare const convertReducersToSectionReducers: (section: string, sectionReducers: IDynaduxReducerDic) => IDynaduxReducerDic; diff --git a/dist/esNext/createStore/convertReducersToSectionReducers.js b/dist/esNext/createStore/convertReducersToSectionReducers.js new file mode 100644 index 0000000..db2f52b --- /dev/null +++ b/dist/esNext/createStore/convertReducersToSectionReducers.js @@ -0,0 +1,27 @@ +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); +}; +export var convertReducersToSectionReducers = function (section, sectionReducers) { + return Object.keys(sectionReducers) + .reduce(function (acc, action) { + var originalReducer = sectionReducers[action]; + acc[action] = function (params) { + var _a; + var subPartialState = originalReducer(__assign(__assign({}, params), { state: params.state[section] })); + if (subPartialState) + return _a = {}, + _a[section] = __assign(__assign({}, params.state[section]), subPartialState), + _a; + }; + return acc; + }, {}); +}; +//# sourceMappingURL=convertReducersToSectionReducers.js.map \ No newline at end of file diff --git a/dist/esNext/createStore/convertReducersToSectionReducers.js.map b/dist/esNext/createStore/convertReducersToSectionReducers.js.map new file mode 100644 index 0000000..c0609c9 --- /dev/null +++ b/dist/esNext/createStore/convertReducersToSectionReducers.js.map @@ -0,0 +1 @@ +{"version":3,"file":"convertReducersToSectionReducers.js","sourceRoot":"","sources":["../../../src/createStore/convertReducersToSectionReducers.ts"],"names":[],"mappings":";;;;;;;;;;;AAKA,MAAM,CAAC,IAAM,gCAAgC,GAAG,UAAC,OAAe,EAAE,eAAwC;IACxG,OAAO,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC;SAChC,MAAM,CAAC,UAAC,GAA4B,EAAE,MAAc;QACnD,IAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QAEhD,GAAG,CAAC,MAAM,CAAC,GAAG,UAAC,MAAoC;;YACjD,IAAM,eAAe,GAAG,eAAe,uBAClC,MAAM,KACT,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAC5B,CAAC;YAEH,IAAI,eAAe;gBAAE,OAAO;oBAC1B,GAAC,OAAO,0BACH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,GACrB,eAAe,CACnB;sBACK,CAAC;QACX,CAAC,CAAC;QAEF,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,EAAE,CAAC,CAAC;AACX,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/esNext/createStore/createStore.d.ts b/dist/esNext/createStore/createStore.d.ts index 7531b2c..9b3ef54 100644 --- a/dist/esNext/createStore/createStore.d.ts +++ b/dist/esNext/createStore/createStore.d.ts @@ -1,8 +1,19 @@ -import { IDynaduxConfig, TDynaduxDispatch } from "../Dynadux/Dynadux"; +import { IDynaduxConfig, TDynaduxDispatch, IDynaduxReducerDic } from "../Dynadux/Dynadux"; export interface ICreateStoreConfig extends IDynaduxConfig { } export interface ICreateStoreAPI { - dispatch: TDynaduxDispatch; state: TState; + dispatch: TDynaduxDispatch; + createSection: (createSectionConfig: ICreateSectionConfig) => ICreateSectionAPI; +} +export interface ICreateSectionConfig { + section: string; + initialState: TSectionState; + reducers: IDynaduxReducerDic; +} +export interface ICreateSectionAPI { + storeState: TState; + state: TSectionState; + dispatch: TDynaduxDispatch; } export declare const createStore: (config: ICreateStoreConfig) => ICreateStoreAPI; diff --git a/dist/esNext/createStore/createStore.js b/dist/esNext/createStore/createStore.js index 2232d70..4c64461 100644 --- a/dist/esNext/createStore/createStore.js +++ b/dist/esNext/createStore/createStore.js @@ -1,9 +1,28 @@ import { Dynadux, } from "../Dynadux/Dynadux"; +import { convertReducersToSectionReducers } from "./convertReducersToSectionReducers"; export var createStore = function (config) { var dynadux = new Dynadux(config); return { + get state() { + return dynadux.state; + }, dispatch: dynadux.dispatch, - get state() { return dynadux.state; }, + createSection: function (createSectionConfig) { + var section = createSectionConfig.section, initialState = createSectionConfig.initialState, reducers = createSectionConfig.reducers; + if (dynadux.state[section]) + throw new Error("dynadux: createSection: Section or root property \"" + section + "\" already exists, section couldn't be created."); + dynadux.setSectionInitialState(section, initialState); + dynadux.addReducers(convertReducersToSectionReducers(section, reducers)); + return { + get storeState() { + return dynadux.state; + }, + get state() { + return dynadux.state[section]; + }, + dispatch: dynadux.dispatch, + }; + } }; }; //# sourceMappingURL=createStore.js.map \ No newline at end of file diff --git a/dist/esNext/createStore/createStore.js.map b/dist/esNext/createStore/createStore.js.map index 09962c9..f0691b2 100644 --- a/dist/esNext/createStore/createStore.js.map +++ b/dist/esNext/createStore/createStore.js.map @@ -1 +1 @@ -{"version":3,"file":"createStore.js","sourceRoot":"","sources":["../../../src/createStore/createStore.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,GAGR,MAAM,oBAAoB,CAAC;AAU5B,MAAM,CAAC,IAAM,WAAW,GAAG,UAAe,MAAkC;IAC1E,IAAM,OAAO,GAAG,IAAI,OAAO,CAAS,MAAM,CAAC,CAAC;IAE5C,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,IAAI,KAAK,KAAK,OAAQ,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;KACvC,CAAC;AACJ,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"createStore.js","sourceRoot":"","sources":["../../../src/createStore/createStore.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,GAIR,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AAuBtF,MAAM,CAAC,IAAM,WAAW,GAAG,UAAe,MAAkC;IAC1E,IAAM,OAAO,GAAG,IAAI,OAAO,CAAS,MAAM,CAAC,CAAC;IAE5C,OAAO;QACL,IAAI,KAAK;YACP,OAAO,OAAO,CAAC,KAAK,CAAC;QACvB,CAAC;QACD,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,aAAa,EAAE,UAAgB,mBAAwD;YAEnF,IAAA,qCAAO,EACP,+CAAY,EACZ,uCAAQ,CACc;YAExB,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,wDAAqD,OAAO,oDAAgD,CAAC,CAAC;YAE1J,OAAO,CAAC,sBAAsB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YACtD,OAAO,CAAC,WAAW,CAAC,gCAAgC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;YAEzE,OAAO;gBACL,IAAI,UAAU;oBACZ,OAAO,OAAO,CAAC,KAAK,CAAC;gBACvB,CAAC;gBACD,IAAI,KAAK;oBACP,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAChC,CAAC;gBACD,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC3B,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"} \ No newline at end of file diff --git a/doc/Advanced.md b/doc/Advanced.md index ce4204d..7632f39 100644 --- a/doc/Advanced.md +++ b/doc/Advanced.md @@ -204,6 +204,18 @@ constructor(props) { _**Tip:** Since you use store's `state`, there is no need to pass them to `setState`._ +# Create 3rd party state library + +Dynadux makes easy to create 3rd party libraries. For instance a weather forecast data provider. + +There are two ways for making it: +- Middlewares +- Sections + +With [Middlewares](./Middlewares.md) you can access the Entire Store's State and it is used to monitor and handle a known Store or work in a Generic way. + +With [Sections](./Sections.md) (new in v1.5.0) you can create a Library that will work in its own space and makes the implementation easier. + # Read more - [FAQ](./FAQ.md) Frequently asked questions diff --git a/doc/Debugging.md b/doc/Debugging.md index 7455ef0..0b6e0a2 100644 --- a/doc/Debugging.md +++ b/doc/Debugging.md @@ -186,7 +186,7 @@ Ones you reached this point you are mastering the Dynadux! It is so simple! You can go further reading the Advanced but is not mandatory. -[⬅️ Middlewares](./Middlewares.md) 🔶 [Advanced ➡️](./Advanced.md) +[⬅️ Middlewares](./Middlewares.md) 🔶 [Sections ➡️](./Sections.md) # Read more diff --git a/doc/Middlewares.md b/doc/Middlewares.md index b6ae085..9ecd946 100644 --- a/doc/Middlewares.md +++ b/doc/Middlewares.md @@ -146,6 +146,6 @@ Here, it is easy to make a History State management, navigating back and forth i # Continue -[⬅️ Reducers](../README.md) 🔶 [Debugging ➡️](./Debugging.md) +[⬅️ Reducers](../README.md) 🔶 [Sections ➡️](./Sections.md) [🏠 Home, Contents](../README.md#table-of-contents) diff --git a/doc/Sections.md b/doc/Sections.md new file mode 100644 index 0000000..8249a99 --- /dev/null +++ b/doc/Sections.md @@ -0,0 +1,245 @@ +[🏠 Home](../README.md) + +# Dynadux - Sections + +Sections are new in 1.5.0 and help to create states for applications or big components. It is not required to use it. + +Once we create a store we can create a section that will be scoped on the specific root property of the Store's State. + +The implementation of a Section is the same as of a Store. + +The benefit of Sections is that reducers cannot access the state of the other sections or the state of the app. + +[Full example](../tests/scripts/sections.test.ts) + +# Principals + +- Each Section is creating a property on the root level of State. +- Reducers can access the state of the root property only. +- The provided state from a Section is the state of the root property. +- You can still pass the entire state of the app manually but this is not recommended. + +# Example how to use them + +Imagine we have a To-Do app, with a user login feature. + +One section is the User feature. This keeps information if the user is logged, the avatar of the user, etc.. + +Another section is the To-Do feature. + +These two sections and features are completely decoupled. The concept is that the To-Do feature should work on any kind of application decoupled from User Handling. + +On the application layer, when the user is logged, the app will ask from the To-Do to fetch the to-do items for this user id. + +# Steps to work with Sections + +_Examples are in Typescript_ + +### #1 The Create Section function + +Create Section function is a function that +- takes the created store as argument _and_ +- returns a business API to use it later in the application. + +From the passed store reference, we use the `createSection` function that requires: +- `section: string`, the name of the section +- `initialState`, the initial state of the section +- `reducers`, the reducers scoped the section + +What is new here is only the `section` string. +This will be the root property name in the Store's state. +But in practice, we won't use it, because in the end, the state will be accessed from the return of this function. +_We will see that later._ + +Initial state and reducers are remaining exactly the same as we learned in the previous chapters. There is really nothing new to learn. +``` +const createUserInfoSection = (store: ICreateStoreAPI) => { + const section = store.`createSection`({ + section: 'userSection', + initialState: { + logged: false, + name: '', + avatar: '', + loggedAt: '', + }, + reducers: { + [EUserActions.LOGIN]: ({payload}) => { + const { + name, + avatar, + }: ILOGIN_payload = payload; + return { + logged: true, + name, + avatar, + loggedAt: "today", + }; + }, + [EUserActions.LOGOUT]: ({state: {logged}}) => { + if (!logged) return; // Exit. No need to change anything. + return { + logged: false, + name: '', + avatar: '', + loggedAt: "today", + }; + }, + [EUserActions.UPDATE_AVATAR]: ({payload: avatar}) => ({avatar}), + }, + }); + + return { + get state() { + return section.state; + }, + actions: { + login: (name: string, avatar: string) => section.dispatch(EUserActions.LOGIN, {name, avatar}), + logout: () => section.dispatch(EUserActions.LOGOUT), + updateAvatar: (avatar: string) => section.dispatch(EUserActions.UPDATE_AVATAR, avatar), + } + }; +}; +``` + +Let's create another one section, for the To-Do feature. + +``` +const createTodosSection = (store: ICreateStoreAPI) => { + const section = store.createSection({ + section: 'todosSection', + initialState: { + todos: [], + lastAddedTodo: '', + }, + reducers: { + [ETodosActions.ADD_TODO]: ({state: {todos}, payload}) => { + const {id, label}: IADD_TODO_payload = payload; + return { + todos: todos.concat({id, label, done: false}), + lastAddedTodo: "today", + }; + }, + [ETodosActions.REMOVE_TODO]: ({state: {todos}, payload: id}) => { + return { + todos: todos.filter(todo => todo.id !== id), + }; + }, + [ETodosActions.COMPLETE_TODO]: ({state: {todos}, payload: id}) => { + return { + todos: todos.map(todo => { + if (todo.id === id) return ({...todo, done: true}); + return todo; + }), + }; + }, + }, + }); + + return { + get state() { + return section.state; + }, + actions: { + addTodo: (id: number, label: string) => section.dispatch(ETodosActions.ADD_TODO, {id, label}), + removeTodo: (id: number) => section.dispatch(ETodosActions.REMOVE_TODO, id), + completeTodo: (id: number) => section.dispatch(ETodosActions.COMPLETE_TODO, id), + } + }; +}; +``` + +### #2 The Create App Store function + +This Create App Store +- requests an onChange callback to get notified for the changes +- returns an api to use the store. + +The returned API will be directly the API of the Sections. + +So, we create the Dynadux store, with the classic `createStore`, but we don't pass reducers for the sections. + +As an output of the function, we return a small API. For each Section, we create a property with the returned value of the create section function. The function of each section requires the Store in order to be attached to this store. + +This is the function to create the app store. + +``` +const createAppStore = (onChange: (state: IAppState) => void) => { + const store = createStore({ + onChange, + }); + + return { + user: createUserInfoSection(store), + todos: createTodosSection(store), + }; +}; +``` + +That's all! The app´s store is nothing but a concatenation of Sections. + +### #3 Usage of our app store + +``` +// Create the app's store +const store = createAppStore(this.setState.bind(this)); + +// Call actions +store.todos.actions.addTodo(101, 'Before work beers'); +store.todos.actions.addTodo(102, 'After work beers'); +store.todos.actions.removeTodo(101); + +// Access sections state +store.user.state // The user's info +store.todos.state.todos // The array with current todos + +``` + +# `createSection()` API + +The `createSection` methods require a config object of this interface: + +``` +ICreateSectionConfig { + section: string; + initialState: TSectionState; + reducers: IDynaduxReducerDic; +} +``` + +The method returns an object of this interface: + +``` +ICreateSectionAPI { + storeState: TState; + state: TSectionState; + dispatch: (action: string, payload: TPayload): void; +} +``` + +Through the `storeState` getter you can get the State of the Store. +That means that you can access one level up data are unknown for the Section. +In some cases, this might be needed. + +Keep in mind that accessing the Store's State makes the Section dependent on the app's State. +Keeping Section decoupled from the Store's state makes it reusable in other apps. + +# Sum up + +Sections help to create isolated state scopes. + +Sections are consisted of +- initial section state +- classic reducers +- the output of an API for being used from the app + +We create a Dynadux store. As an App store, we return an object with the returned API of each Section. + +[Full example](../tests/scripts/sections.test.ts) + +# Continue + +[⬅️ Middlewares](./Middlewares.md) 🔶 [Debugging ➡️](./Debugging.md) + +[🏠 Home, Contents](../README.md#table-of-contents) + + diff --git a/package.json b/package.json index 9da425a..f60abac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dynadux", - "version": "1.4.3", + "version": "1.5.1", "description": "The Dynadux", "repository": { "type": "git", diff --git a/src/Dynadux/Dynadux.ts b/src/Dynadux/Dynadux.ts index d32423e..174ba2b 100644 --- a/src/Dynadux/Dynadux.ts +++ b/src/Dynadux/Dynadux.ts @@ -2,7 +2,7 @@ import {combineMultipleReducers} from "../utils/combineMultipleReducers"; export interface IDynaduxConfig { initialState?: TState; - reducers: IDynaduxReducerDic | IDynaduxReducerDic[]; + reducers?: IDynaduxReducerDic | IDynaduxReducerDic[]; middlewares?: IDynaduxMiddleware[]; onDispatch?: (action: string, payload: any) => void; onChange?: (state: TState) => void; @@ -54,20 +54,21 @@ export class Dynadux { private _state: TState; private readonly _dispatches: IDispatch[] = []; private _isDispatching = false; - private readonly _reducers: IDynaduxReducerDic; + private _reducers: IDynaduxReducerDic; constructor(private readonly _config: IDynaduxConfig) { const { initialState = {}, + reducers = {}, middlewares = [], } = this._config; this._state = initialState as any; this._reducers = - Array.isArray(this._config.reducers) - ? combineMultipleReducers(...this._config.reducers) - : this._config.reducers; + Array.isArray(reducers) + ? combineMultipleReducers(...reducers) + : reducers; middlewares.forEach(middleware => middleware.init && middleware.init(this)); } @@ -76,10 +77,18 @@ export class Dynadux { return this._state; } + public setSectionInitialState(section: string, sectionState: any): void { + this._state[section] = sectionState; + } + + public addReducers = (reducers: IDynaduxReducerDic): void => { + this._reducers = combineMultipleReducers(this._reducers, reducers); + }; + public dispatch = (action: string, payload: TPayload): void => { this._dispatches.push({action, payload}); this._dispatch(); - } + }; private _dispatch = (): void => { if (this._isDispatching) return; @@ -145,5 +154,5 @@ export class Dynadux { this._isDispatching = false; this._dispatch(); - } + }; } diff --git a/src/createStore/convertReducersToSectionReducers.ts b/src/createStore/convertReducersToSectionReducers.ts new file mode 100644 index 0000000..95ad87f --- /dev/null +++ b/src/createStore/convertReducersToSectionReducers.ts @@ -0,0 +1,27 @@ +import { + IDynaduxReducerDic, + IDynaduxReducerAPI +} from "../Dynadux/Dynadux"; + +export const convertReducersToSectionReducers = (section: string, sectionReducers: IDynaduxReducerDic): IDynaduxReducerDic => { + return Object.keys(sectionReducers) + .reduce((acc: IDynaduxReducerDic, action: string) => { + const originalReducer = sectionReducers[action]; + + acc[action] = (params: IDynaduxReducerAPI): undefined | void | Partial => { + const subPartialState = originalReducer({ + ...params, + state: params.state[section], + }); + + if (subPartialState) return { + [section]: { + ...params.state[section], + ...subPartialState + }, + } as any; + }; + + return acc; + }, {}); +}; diff --git a/src/createStore/createStore.ts b/src/createStore/createStore.ts index dcd27b9..cfe3f10 100644 --- a/src/createStore/createStore.ts +++ b/src/createStore/createStore.ts @@ -2,22 +2,61 @@ import { Dynadux, IDynaduxConfig, TDynaduxDispatch, + IDynaduxReducerDic, } from "../Dynadux/Dynadux"; +import { convertReducersToSectionReducers } from "./convertReducersToSectionReducers"; export interface ICreateStoreConfig extends IDynaduxConfig { } export interface ICreateStoreAPI { - dispatch: TDynaduxDispatch; state: TState; + dispatch: TDynaduxDispatch; + createSection: (createSectionConfig: ICreateSectionConfig) => ICreateSectionAPI; +} + +export interface ICreateSectionConfig { + section: string; + initialState: TSectionState; + reducers: IDynaduxReducerDic; +} + +export interface ICreateSectionAPI { + storeState: TState; + state: TSectionState; + dispatch: TDynaduxDispatch; } export const createStore = (config: ICreateStoreConfig): ICreateStoreAPI => { const dynadux = new Dynadux(config); return { + get state() { + return dynadux.state; + }, dispatch: dynadux.dispatch, - get state() { return dynadux.state; }, + createSection: (createSectionConfig: ICreateSectionConfig): ICreateSectionAPI => { + const { + section, + initialState, + reducers, + } = createSectionConfig; + + if (dynadux.state[section]) throw new Error(`dynadux: createSection: Section or root property "${section}" already exists, section couldn't be created.`); + + dynadux.setSectionInitialState(section, initialState); + dynadux.addReducers(convertReducersToSectionReducers(section, reducers)); + + return { + get storeState(): TState { + return dynadux.state; + }, + get state(): TSectionState { + return dynadux.state[section]; + }, + dispatch: dynadux.dispatch, + }; + } }; }; diff --git a/tests/scripts/__snapshots__/sections.test.ts.snap b/tests/scripts/__snapshots__/sections.test.ts.snap new file mode 100644 index 0000000..5ee5b5a --- /dev/null +++ b/tests/scripts/__snapshots__/sections.test.ts.snap @@ -0,0 +1,105 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Dynadux Working with Sections: After avatar update 1`] = ` +Object { + "todosSection": Object { + "lastAddedTodo": "today", + "todos": Array [ + Object { + "done": true, + "id": 102, + "label": "After work beers", + }, + ], + }, + "userSection": Object { + "avatar": "https://www.anel.co/user/928457123/profile-02.png", + "logged": true, + "loggedAt": "today", + "name": "John", + }, +} +`; + +exports[`Dynadux Working with Sections: After complete 102 1`] = ` +Object { + "todosSection": Object { + "lastAddedTodo": "today", + "todos": Array [ + Object { + "done": true, + "id": 102, + "label": "After work beers", + }, + ], + }, + "userSection": Object { + "avatar": "https://www.anel.co/user/928457123/profile-01.png", + "logged": true, + "loggedAt": "today", + "name": "John", + }, +} +`; + +exports[`Dynadux Working with Sections: After remove of 101 todo 1`] = ` +Object { + "todosSection": Object { + "lastAddedTodo": "today", + "todos": Array [ + Object { + "done": false, + "id": 102, + "label": "After work beers", + }, + ], + }, + "userSection": Object { + "avatar": "https://www.anel.co/user/928457123/profile-01.png", + "logged": true, + "loggedAt": "today", + "name": "John", + }, +} +`; + +exports[`Dynadux Working with Sections: First todos 1`] = ` +Object { + "todosSection": Object { + "lastAddedTodo": "today", + "todos": Array [ + Object { + "done": false, + "id": 101, + "label": "Before work beers", + }, + Object { + "done": false, + "id": 102, + "label": "After work beers", + }, + ], + }, + "userSection": Object { + "avatar": "https://www.anel.co/user/928457123/profile-01.png", + "logged": true, + "loggedAt": "today", + "name": "John", + }, +} +`; + +exports[`Dynadux Working with Sections: Initial state 1`] = ` +Object { + "todosSection": Object { + "lastAddedTodo": "", + "todos": Array [], + }, + "userSection": Object { + "avatar": "", + "logged": false, + "loggedAt": "", + "name": "", + }, +} +`; diff --git a/tests/scripts/sections.test.ts b/tests/scripts/sections.test.ts new file mode 100644 index 0000000..1b5c1dd --- /dev/null +++ b/tests/scripts/sections.test.ts @@ -0,0 +1,193 @@ +import "jest"; +import { + ICreateStoreAPI, + createStore +} from "../../src/createStore/createStore"; + +// User Section store + +interface IUserInfoState { + logged: boolean; + name: string; + avatar: string; + loggedAt: string; +} + +enum EUserActions { + LOGIN = "LOGIN", // payload: ILOGIN_payload + LOGOUT = "LOGOUT", + UPDATE_AVATAR = "UPDATE_AVATAR", // payload: string: new url +} + +interface ILOGIN_payload { + name: string; + avatar: string; +} + +const createUserInfoSection = (store: ICreateStoreAPI) => { + const section = store.createSection({ + section: 'userSection', + initialState: { + logged: false, + name: '', + avatar: '', + loggedAt: '', + }, + reducers: { + [EUserActions.LOGIN]: ({payload}) => { + const { + name, + avatar, + }: ILOGIN_payload = payload; + return { + logged: true, + name, + avatar, + loggedAt: "today", + }; + }, + [EUserActions.LOGOUT]: ({state: {logged}}) => { + if (!logged) return; // Exit. No need to change anything. + return { + logged: false, + name: '', + avatar: '', + loggedAt: "today", + }; + }, + [EUserActions.UPDATE_AVATAR]: ({payload: avatar}) => ({avatar}), + }, + }); + + return { + get state() { + return section.state; + }, + actions: { + login: (name: string, avatar: string) => section.dispatch(EUserActions.LOGIN, {name, avatar}), + logout: () => section.dispatch(EUserActions.LOGOUT), + updateAvatar: (avatar: string) => section.dispatch(EUserActions.UPDATE_AVATAR, avatar), + } + }; +}; + +// Todos Section store + +interface ITodosManagementState { + todos: ITodo[]; + lastAddedTodo: string; +} + +interface ITodo { + id: number; + label: string; + done: boolean; +} + +enum ETodosActions { + ADD_TODO = "ADD_TODO", // payload: IADD_TODO_payload + REMOVE_TODO = "REMOVE_TODO", // payload: number: the id of the todo + COMPLETE_TODO = "COMPLETE_TODO", // payload: number: the id of the todo +} + +interface IADD_TODO_payload { + id: number; + label: string; +} + +const createTodosSection = (store: ICreateStoreAPI) => { + const section = store.createSection({ + section: 'todosSection', + initialState: { + todos: [], + lastAddedTodo: '', + }, + reducers: { + [ETodosActions.ADD_TODO]: ({state: {todos}, payload}) => { + const {id, label}: IADD_TODO_payload = payload; + return { + todos: todos.concat({id, label, done: false}), + lastAddedTodo: "today", + }; + }, + [ETodosActions.REMOVE_TODO]: ({state: {todos}, payload: id}) => { + return { + todos: todos.filter(todo => todo.id !== id), + }; + }, + [ETodosActions.COMPLETE_TODO]: ({state: {todos}, payload: id}) => { + return { + todos: todos.map(todo => { + if (todo.id === id) return ({...todo, done: true}); + return todo; + }), + }; + }, + }, + }); + + return { + get state() { + return section.state; + }, + actions: { + addTodo: (id: number, label: string) => section.dispatch(ETodosActions.ADD_TODO, {id, label}), + removeTodo: (id: number) => section.dispatch(ETodosActions.REMOVE_TODO, id), + completeTodo: (id: number) => section.dispatch(ETodosActions.COMPLETE_TODO, id), + } + }; + +}; + +// App store + +interface IAppState { + userSection: IUserInfoState; + todosSection: ITodosManagementState; +} + +const createAppStore = (onChange: (state: IAppState) => void) => { + const store = createStore({ + reducers: {}, + onChange, + }); + + return { + get state() { + return store.state; + }, + user: createUserInfoSection(store), + todos: createTodosSection(store), + }; +}; + + +describe('Dynadux', () => { + test('Working with Sections', () => { + let stateChanged = 0; + + const store = createAppStore(state => stateChanged++); + + expect(store.state).toMatchSnapshot('Initial state'); + + store.user.actions.login('John', 'https://www.anel.co/user/928457123/profile-01.png'); + store.todos.actions.addTodo(101, 'Before work beers'); + store.todos.actions.addTodo(102, 'After work beers'); + + expect(store.state).toMatchSnapshot('First todos'); + + store.todos.actions.removeTodo(101); + + expect(store.state).toMatchSnapshot('After remove of 101 todo'); + + store.todos.actions.completeTodo(102); + + expect(store.state).toMatchSnapshot('After complete 102'); + + store.user.actions.updateAvatar('https://www.anel.co/user/928457123/profile-02.png'); + + expect(store.state).toMatchSnapshot('After avatar update'); + + expect(stateChanged).toBe(6); + }); +}); diff --git a/tslint.json b/tslint.json index df3be11..849b0cc 100644 --- a/tslint.json +++ b/tslint.json @@ -1,6 +1,6 @@ { "jsRules": { - "class-name": true, + "class-name": false, "comment-format": [ true, "check-space" @@ -40,7 +40,7 @@ ] }, "rules": { - "class-name": true, + "class-name": false, "comment-format": [ true, "check-space" @@ -61,7 +61,8 @@ ], "semicolon": [ true, - "always" + "always", + "strict-bound-class-methods" ], "triple-equals": [ true,