Skip to content

Commit 3fe1c64

Browse files
committed
feat: add effects method of Builder
1 parent b51e39e commit 3fe1c64

File tree

5 files changed

+136
-4
lines changed

5 files changed

+136
-4
lines changed

README.md

+62-4
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,44 @@ export const parentModel = build()
113113

114114
Then every time you dispatch `Child1.increment` the `Child2.decrement` actoin will be dispatched and handled too and vice versa.
115115

116+
### Dispatching no object action
117+
If you need to dispatch actions which are not simple objects, for example, functions with using redux-think, you can use the `effects` and `effect` methods of a Builder.
118+
```js
119+
import { build } from 'encaps';
120+
121+
export const model = build()
122+
.initState(() => ({value: true}))
123+
.handlers({
124+
set: (state, {payload}) => ({...state, value: payload})
125+
})
126+
.affect(
127+
'thunk1',
128+
(actions) => () => (dispatch) => {
129+
...
130+
/** some async code */
131+
...
132+
dispatch(actions.set(false))
133+
}
134+
)
135+
.affects({
136+
thunk2: (actions) => () => (dispatch) => {
137+
...
138+
/** some async code */
139+
...
140+
dispatch(actions.set(true))
141+
},
142+
thunk3: (actions) => (payload) => (dispatch) => {
143+
...
144+
/** some async code */
145+
...
146+
dispatch(actions.set(payload))
147+
},
148+
})
149+
150+
dispatch(model.actions.thunk3(false));
151+
```
152+
An effect is a function which receives actions of the current builder. It should return an action creator. And this action creator can return a function, a promise or something else.
153+
116154
### Dynamic list of children
117155

118156
#### Array of children
@@ -172,7 +210,7 @@ map.reducer(initState, map.actions.add('Child1'));
172210
```
173211

174212
## API
175-
The main idea of this package it to build independent modeles that consists of action creators and a reducer.
213+
The main idea of this package it to build independent modeles which consists of action creators and a reducer.
176214

177215
```typescript
178216
interface Model {
@@ -185,7 +223,7 @@ interface Model {
185223
}
186224
```
187225

188-
The `actions` field of model is a map that contains of functions which get payload and return action. Action creators can be nested.
226+
The `actions` field of model is a map which contains of functions which get payload and return action. Action creators can be nested.
189227

190228
```typescript
191229
interface ActionCreators {
@@ -211,7 +249,7 @@ The `build` function returns a `Builder` object.
211249
```typescript
212250
interface Builder {
213251
/**
214-
* You can set function that create initial state
252+
* You can set function which create initial state
215253
* This function gets state created by previous initState function (it can be used then you extends existing model).
216254
* @returns new Builder
217255
*/
@@ -250,9 +288,29 @@ interface Builder {
250288
* @returns new Builder
251289
*/
252290
subActions(
253-
/** map of functions that create additional actions */
291+
/** map of functions which create additional actions */
254292
wrapers: {[K: string]: (action, actions) => Action}
255293
): Builder;
294+
295+
/**
296+
* adds action creator which can return something different from simple object
297+
* @returns new builder
298+
*/
299+
effect(
300+
/** action key */
301+
key: K,
302+
/** */
303+
effect: (actions) => (...args) => any
304+
): Builder;
305+
306+
/**
307+
* adds action creators which can return something different from simple object
308+
* @returns new builder
309+
*/
310+
effects(
311+
/** map of affects */
312+
effects: {[K: string]: (actions) => (...args) => any}
313+
): Builder;
256314
}
257315
```
258316

dist/controller.d.ts

+9
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ export interface IBuilder<Actions extends IActionCreators = {}, State = {}> exte
9797
effect: (actions: Actions) => (...args: P) => A): IBuilder<Actions & {
9898
[F in K]: (...args: P) => A;
9999
}, State>;
100+
/**
101+
* Позволяет создавать любые действия, не только простые объекты
102+
* @returns новый строитель
103+
*/
104+
effects<EF extends Dictionary<(actions: Actions) => (...args: any[]) => any>>(
105+
/** ассоциативный массив дочерних моделей */
106+
effects: EF): IBuilder<Actions & {
107+
[C in keyof EF]: ReturnType<EF[C]>;
108+
}, State>;
100109
}
101110
export declare function getSubActions(action: IAction<any>): IAction<any>[];
102111
export declare const unwrapAction: (action: IAction<any>) => {

dist/controller.js

+11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ var Builder = /** @class */ (function () {
1111
function Builder(_model) {
1212
this._model = _model;
1313
}
14+
/** @deprecated Will be removed in the next version. Use initState instead. */
1415
Builder.prototype.setInitState = function (f) {
1516
if (console && typeof console.warn === 'function') {
1617
console.warn('"setInitState" method is deprecated and will be removed in the next version. Use "initState" instead.');
@@ -85,6 +86,16 @@ var Builder = /** @class */ (function () {
8586
var _a;
8687
return new Builder(tslib_1.__assign({}, this.model, { actions: tslib_1.__assign({}, this.model.actions, (_a = {}, _a[key] = createEffect(effect, function () { return _this.model.actions; }), _a)) }));
8788
};
89+
/**
90+
* Позволяет создавать любые действия, не только простые объекты
91+
* @returns новый строитель
92+
*/
93+
Builder.prototype.effects = function (
94+
/** ассоциативный массив дочерних моделей */
95+
effects) {
96+
/** @todo оптимизировать */
97+
return Object.keys(effects).reduce(function (newBuilder, key) { return newBuilder.effect(key, effects[key]); }, this);
98+
};
8899
Object.defineProperty(Builder.prototype, "model", {
89100
get: function () {
90101
return tslib_1.__assign({}, this._model);

src/controller.ts

+30
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,18 @@ export interface IBuilder<
121121
/** Функция, которая создает действия не в виде простых объектов */
122122
effect: (actions: Actions) => (...args: P) => A
123123
): IBuilder<Actions & {[F in K]: (...args: P) => A}, State>;
124+
125+
/**
126+
* Позволяет создавать любые действия, не только простые объекты
127+
* @returns новый строитель
128+
*/
129+
effects<EF extends Dictionary<(actions: Actions) => (...args: any[]) => any>>(
130+
/** ассоциативный массив дочерних моделей */
131+
effects: EF
132+
): IBuilder<
133+
Actions & {[C in keyof EF]: ReturnType<EF[C]>},
134+
State
135+
>;
124136
}
125137

126138
/**
@@ -245,6 +257,24 @@ class Builder<
245257
});
246258
}
247259

260+
/**
261+
* Позволяет создавать любые действия, не только простые объекты
262+
* @returns новый строитель
263+
*/
264+
effects<EF extends Dictionary<(actions: Actions) => (...args: any[]) => any>>(
265+
/** ассоциативный массив дочерних моделей */
266+
effects: EF
267+
): IBuilder<
268+
Actions & {[C in keyof EF]: ReturnType<EF[C]>},
269+
State
270+
> {
271+
/** @todo оптимизировать */
272+
return Object.keys(effects).reduce(
273+
(newBuilder, key) => newBuilder.effect(key, effects[key]),
274+
this as any
275+
);
276+
}
277+
248278
get model(): IModel<Actions, State> {
249279
return {...this._model};
250280
}

test/controller.spec.ts

+24
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,30 @@ test('simple effects', (t) => {
637637
t.end();
638638
});
639639

640+
test('simple multiple effects', (t) => {
641+
const model = build()
642+
.initState((state) => ({...state, value: 0}))
643+
.handlers({
644+
simpleAction: (state, action: IAction<number>) => ({...state, value: action.payload})
645+
})
646+
.effects({
647+
ef1: (actions) => (payload: number) => () => actions.simpleAction(payload * 2),
648+
ef2: (actions) => (payload: number) => () => actions.simpleAction(payload / 2),
649+
});
650+
651+
t.deepEqual(
652+
model.actions.ef1(10)(),
653+
{type: 'simpleAction', payload: 20}
654+
);
655+
656+
t.deepEqual(
657+
model.actions.ef2(10)(),
658+
{type: 'simpleAction', payload: 5}
659+
);
660+
661+
t.end();
662+
});
663+
640664
test('children effects', (t) => {
641665
const grandChild = build()
642666
.initState((state) => ({...state, value: 0}))

0 commit comments

Comments
 (0)