Skip to content

Commit 4c84b82

Browse files
committed
feat: add effects
1 parent fcbb158 commit 4c84b82

14 files changed

+278
-47
lines changed

dist/controller.d.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,11 @@ export declare function addSubActions<T extends IActionCreators>(actions: T, wra
7474
export declare function decomposeKeys(list: object, parentKey?: string): {
7575
[key: string]: any;
7676
};
77+
export declare function createEffect(effect: (actions: any) => any, getActions: (...agrs: any[]) => any): (...args: any[]) => any;
78+
export declare function wrapEffect(wrapActions: any, effect: any): (...args: any[]) => any;
79+
export declare function isEffect(getter: any): boolean;
80+
/** @deprecated will be removed in the next version. Use createEffect instead. */
7781
export declare function markAsActionCreatorsGetter(getter: any): any;
78-
export declare function isActionCreatorsGetter(getter: any): boolean;
7982
export declare function build(): IBuilder;
8083
export declare function build<Actions extends IActionCreators, State>(model: IModel<Actions, State>): IBuilder<Actions, State>;
8184
export {};

dist/controller.js

+42-16
Original file line numberDiff line numberDiff line change
@@ -102,26 +102,23 @@ exports.unwrapAction = function (action) {
102102
};
103103
};
104104
function wrapChildActionCreators(wrap, actions) {
105-
return Object.keys(actions).reduce(function (result, actionKey) {
105+
var wrappedActions = Object.keys(actions).reduce(function (result, actionKey) {
106106
var _a, _b, _c;
107107
if (typeof actions[actionKey] === 'function') {
108-
if (isActionCreatorsGetter(actions[actionKey])) {
109-
return (tslib_1.__assign({}, result, (_a = {}, _a[actionKey] = markAsActionCreatorsGetter(function () {
110-
var args = [];
111-
for (var _i = 0; _i < arguments.length; _i++) {
112-
args[_i] = arguments[_i];
113-
}
114-
return wrapChildActionCreators(wrap, actions[actionKey].apply(actions, args));
115-
}), _a)));
108+
if (isEffect(actions[actionKey])) {
109+
return (tslib_1.__assign({}, result, (_a = {}, _a[actionKey] = wrapEffect(function (actions) { return wrapChildActionCreators(wrap, actions); }, actions[actionKey]), _a)));
116110
}
117111
else {
112+
// обычные действия
118113
return (tslib_1.__assign({}, result, (_b = {}, _b[actionKey] = function (payload) { return wrap(actions[actionKey](payload)); }, _b)));
119114
}
120115
}
121116
else {
117+
// действия дочерних объектов
122118
return (tslib_1.__assign({}, result, (_c = {}, _c[actionKey] = wrapChildActionCreators(wrap, actions[actionKey]), _c)));
123119
}
124120
}, {});
121+
return wrappedActions;
125122
}
126123
exports.wrapChildActionCreators = wrapChildActionCreators;
127124
function wrapAction(key) {
@@ -164,17 +161,46 @@ function decomposeKeys(list, parentKey) {
164161
}, {});
165162
}
166163
exports.decomposeKeys = decomposeKeys;
167-
/** @todo продумать, как по-другому определять, что функция возвращает создателей действий, а не действия */
168-
var ActionCreatorsGetter = '__ActionCreatorsGetter__';
164+
var CheckEffectField = '__Encaps.ActionCreatorsGetter__';
165+
var GetEffectParamsValue = '__Encaps.GetEffectParamsValue__';
166+
function createEffect(effect, getActions) {
167+
var newEffect = function () {
168+
var args = [];
169+
for (var _i = 0; _i < arguments.length; _i++) {
170+
args[_i] = arguments[_i];
171+
}
172+
if (args[0] === GetEffectParamsValue) {
173+
return [effect, getActions];
174+
}
175+
else {
176+
return effect(getActions.apply(void 0, args)).apply(void 0, args);
177+
}
178+
};
179+
newEffect[CheckEffectField] = true;
180+
return newEffect;
181+
}
182+
exports.createEffect = createEffect;
183+
function wrapEffect(wrapActions, effect) {
184+
var _a = effect(GetEffectParamsValue), originEffect = _a[0], getActions = _a[1];
185+
return createEffect(originEffect, function () {
186+
var args = [];
187+
for (var _i = 0; _i < arguments.length; _i++) {
188+
args[_i] = arguments[_i];
189+
}
190+
return wrapActions(getActions.apply(void 0, args));
191+
});
192+
}
193+
exports.wrapEffect = wrapEffect;
194+
function isEffect(getter) {
195+
return !!getter[CheckEffectField];
196+
}
197+
exports.isEffect = isEffect;
198+
/** @deprecated will be removed in the next version. Use createEffect instead. */
169199
function markAsActionCreatorsGetter(getter) {
170-
getter[ActionCreatorsGetter] = true;
200+
getter[CheckEffectField] = true;
171201
return getter;
172202
}
173203
exports.markAsActionCreatorsGetter = markAsActionCreatorsGetter;
174-
function isActionCreatorsGetter(getter) {
175-
return !!getter[ActionCreatorsGetter];
176-
}
177-
exports.isActionCreatorsGetter = isActionCreatorsGetter;
178204
function build(model) {
179205
if (model === void 0) { model = { actions: {}, reducer: function (s) {
180206
if (s === void 0) { s = {}; }

dist/index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export { IAction, IActionCreator, Reducer, ModelActions, ModelState } from "./types";
2-
export { build, markAsActionCreatorsGetter, unwrapAction } from "./controller";
2+
export { build, markAsActionCreatorsGetter, unwrapAction, createEffect } from "./controller";
33
export { createList } from "./list";
44
export { createMap } from "./map";

dist/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ var controller_1 = require("./controller");
44
exports.build = controller_1.build;
55
exports.markAsActionCreatorsGetter = controller_1.markAsActionCreatorsGetter;
66
exports.unwrapAction = controller_1.unwrapAction;
7+
exports.createEffect = controller_1.createEffect;
78
var list_1 = require("./list");
89
exports.createList = list_1.createList;
910
var map_1 = require("./map");

dist/list.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ var controller_1 = require("./controller");
55
function createList(model) {
66
var list = controller_1.build({
77
actions: {
8-
item: controller_1.markAsActionCreatorsGetter(function (index) { return controller_1.wrapChildActionCreators(controller_1.wrapAction(controller_1.joinKeys('item', index)), model.actions); })
8+
item: controller_1.createEffect(function (actions) { return function () { return actions; }; }, function (index) { return controller_1.wrapChildActionCreators(controller_1.wrapAction(controller_1.joinKeys('item', index)), model.actions); })
99
},
1010
reducer: function (state, baseAction) {
1111
if (state === void 0) { state = { items: [] }; }

dist/map.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ var controller_1 = require("./controller");
55
function createMap(model) {
66
var map = controller_1.build({
77
actions: {
8-
item: controller_1.markAsActionCreatorsGetter(function (index) { return controller_1.wrapChildActionCreators(controller_1.wrapAction(controller_1.joinKeys('item', index)), model.actions); })
8+
item: controller_1.createEffect(function (actions) { return function () { return actions; }; }, function (index) { return controller_1.wrapChildActionCreators(controller_1.wrapAction(controller_1.joinKeys('item', index)), model.actions); })
99
},
1010
reducer: function (state, baseAction) {
1111
if (state === void 0) { state = { items: {} }; }

src/controller.ts

+43-12
Original file line numberDiff line numberDiff line change
@@ -191,25 +191,33 @@ export const unwrapAction = (action: IAction<any>): { action: IAction<any>; key:
191191
}
192192

193193
export function wrapChildActionCreators(wrap: (action: IAction<any>) => IAction<any>, actions) {
194-
return Object.keys(actions).reduce(
194+
const wrappedActions = Object.keys(actions).reduce(
195195
(result, actionKey) => {
196196
if (typeof actions[actionKey] === 'function') {
197-
if (isActionCreatorsGetter(actions[actionKey])) {
197+
if (isEffect(actions[actionKey])) {
198198
return ({
199199
...result,
200-
[actionKey]: markAsActionCreatorsGetter(
201-
(...args) => wrapChildActionCreators(wrap, actions[actionKey](...args))
202-
)
200+
201+
[actionKey]: wrapEffect(
202+
(actions) => wrapChildActionCreators(wrap, actions),
203+
actions[actionKey]
204+
),
205+
// [actionKey]: createEffect(
206+
// (...args) => wrapChildActionCreators(wrap, actions[actionKey](...args))
207+
// )
203208
});
204209
} else {
210+
// обычные действия
205211
return ({...result, [actionKey]: (payload?) => wrap(actions[actionKey](payload))});
206212
}
207213
} else {
214+
// действия дочерних объектов
208215
return ({...result, [actionKey]: wrapChildActionCreators(wrap, actions[actionKey])});
209216
}
210217
},
211218
{}
212219
);
220+
return wrappedActions;
213221
}
214222

215223
export function wrapAction(key: string) {
@@ -264,15 +272,38 @@ export function decomposeKeys(list: object, parentKey = ''): {[key: string]: any
264272
);
265273
}
266274

267-
/** @todo продумать, как по-другому определять, что функция возвращает создателей действий, а не действия */
268-
const ActionCreatorsGetter = '__ActionCreatorsGetter__';
269-
export function markAsActionCreatorsGetter(getter) {
270-
getter[ActionCreatorsGetter] = true;
271-
return getter;
275+
const CheckEffectField = '__Encaps.ActionCreatorsGetter__';
276+
const GetEffectParamsValue = '__Encaps.GetEffectParamsValue__';
277+
278+
export function createEffect(
279+
effect: (actions) => any,
280+
getActions: (...agrs) => any
281+
) {
282+
const newEffect = (...args) => {
283+
if (args[0] === GetEffectParamsValue) {
284+
return [effect, getActions];
285+
} else {
286+
return effect(getActions(...args))(...args);
287+
}
288+
}
289+
290+
newEffect[CheckEffectField] = true;
291+
return newEffect;
292+
}
293+
294+
export function wrapEffect(wrapActions, effect) {
295+
const [originEffect, getActions] = effect(GetEffectParamsValue);
296+
return createEffect(originEffect, (...args) => wrapActions(getActions(...args)));
272297
}
273298

274-
export function isActionCreatorsGetter(getter) {
275-
return !!getter[ActionCreatorsGetter];
299+
export function isEffect(getter) {
300+
return !!getter[CheckEffectField];
301+
}
302+
303+
/** @deprecated will be removed in the next version. Use createEffect instead. */
304+
export function markAsActionCreatorsGetter(getter) {
305+
getter[CheckEffectField] = true;
306+
return getter;
276307
}
277308

278309
export function build(): IBuilder;

src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export { IAction, IActionCreator, Reducer, ModelActions, ModelState } from "./types";
2-
export { build, markAsActionCreatorsGetter, unwrapAction } from "./controller";
2+
export { build, markAsActionCreatorsGetter, unwrapAction, createEffect } from "./controller";
33
export { createList } from "./list";
44
export { createMap } from "./map";

src/list.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import {
66
unwrapAction,
77
IModel,
88
IActionCreators,
9-
markAsActionCreatorsGetter
9+
createEffect
1010
} from './controller';
1111
import { IAction } from './types';
1212

1313
export function createList<Actions extends IActionCreators = {}, State = {}>(model: IModel<Actions, State>) {
1414
const list = build<{item: (index: number) => Actions}, {items: State[]}>({
1515
actions: {
16-
item: markAsActionCreatorsGetter(
16+
item: createEffect(
17+
(actions) => () => actions,
1718
(index) => wrapChildActionCreators(wrapAction(joinKeys('item', index)), model.actions)
1819
)
1920
},

src/map.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import {
66
unwrapAction,
77
IModel,
88
IActionCreators,
9-
markAsActionCreatorsGetter
9+
createEffect
1010
} from './controller';
1111
import { IAction } from './types';
1212

1313
export function createMap<Actions extends IActionCreators = {}, State = {}>(model: IModel<Actions, State>) {
1414
const map = build<{item: (key: string) => Actions}, {items: {[key: string]: State}}>({
1515
actions: {
16-
item: markAsActionCreatorsGetter(
16+
item: createEffect(
17+
(actions) => () => actions,
1718
(index) => wrapChildActionCreators(wrapAction(joinKeys('item', index)), model.actions)
1819
)
1920
},

test/list.spec.ts

+75-10
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ import { build, IAction } from '../src';
33
import { createList } from '../src/list';
44

55
const grandChild = build()
6-
.setInitState(() => ({gc: false}))
7-
.action({
8-
gca: (state, {payload}: IAction<boolean>) => ({...state, gc: payload}),
9-
})
6+
.setInitState(() => ({gc: false}))
7+
.action({
8+
gca: (state, {payload}: IAction<boolean>) => ({...state, gc: payload}),
9+
})
1010

1111
const child = build()
12-
.setInitState(() => ({v1: '', v2: 0}))
13-
.action({
14-
a1: (state, {payload}: IAction<string>) => ({...state, v1: payload}),
15-
a2: (state, {payload}: IAction<number>) => ({...state, v2: payload}),
16-
})
17-
.child('GrandChild', grandChild);
12+
.setInitState(() => ({v1: '', v2: 0}))
13+
.action({
14+
a1: (state, {payload}: IAction<string>) => ({...state, v1: payload}),
15+
a2: (state, {payload}: IAction<number>) => ({...state, v2: payload}),
16+
})
17+
.child('GrandChild', grandChild);
1818

1919
test("List actions", (t) => {
2020
const list = createList(child);
@@ -371,3 +371,68 @@ test('List parent action reducer', (t) => {
371371

372372
t.end();
373373
});
374+
375+
376+
test('Nested lists actions', (t) => {
377+
const childList = createList(child);
378+
379+
const parentList = createList(childList);
380+
381+
t.deepEqual(
382+
parentList.actions.item(10).add(3),
383+
{type: 'item.10.add', payload: 3}
384+
);
385+
386+
t.deepEqual(
387+
parentList.actions.item(10).item(5).GrandChild.gca(true),
388+
{type: 'item.10.item.5.GrandChild.gca', payload: true}
389+
);
390+
391+
const grandParent = build().child('List', parentList);
392+
393+
t.deepEqual(
394+
grandParent.actions.List.item(10).add(3),
395+
{type: 'List.item.10.add', payload: 3}
396+
);
397+
398+
t.deepEqual(
399+
grandParent.actions.List.item(10).item(5).GrandChild.gca(true),
400+
{type: 'List.item.10.item.5.GrandChild.gca', payload: true}
401+
);
402+
403+
t.end();
404+
});
405+
406+
test('Nested lists reducer', (t) => {
407+
const childList = createList(child);
408+
409+
const parentList = createList(childList);
410+
411+
t.deepEqual(
412+
parentList.reducer(),
413+
{items: []}
414+
);
415+
416+
t.deepEqual(
417+
parentList.reducer(undefined, parentList.actions.add()),
418+
{items: [{items:[]}]}
419+
);
420+
421+
t.deepEqual(
422+
parentList.reducer(
423+
{items: [{items:[]}]},
424+
parentList.actions.item(0).add()
425+
),
426+
{items: [{items:[{ v1: '', v2: 0, GrandChild: {gc: false} }]}]}
427+
);
428+
429+
t.deepEqual(
430+
parentList.reducer(
431+
{items: [{items:[{ v1: '', v2: 0, GrandChild: {gc: false} }]}]},
432+
parentList.actions.item(0).item(0).GrandChild.gca(true)
433+
),
434+
{items: [{items:[{ v1: '', v2: 0, GrandChild: {gc: true} }]}]}
435+
);
436+
437+
t.end();
438+
});

0 commit comments

Comments
 (0)