1 | import {combineReducers} from 'redux'
|
2 | import {handleActions} from 'redux-actions'
|
3 | import {recursiveEnhanceFun} from 'dura-util'
|
4 | import * as reduxSagaEffects from "redux-saga/effects";
|
5 |
|
6 | class ModelHandler {
|
7 |
|
8 | models = [];
|
9 |
|
10 | pluginHandler = undefined;
|
11 |
|
12 | defaultModels = [
|
13 | {
|
14 | namespace: '@@duraCore',
|
15 | initialState: {
|
16 | count: 0
|
17 | },
|
18 | reducers: {
|
19 | onChangeState(state) {
|
20 | return ({...state, count: state.count + 1});
|
21 | }
|
22 | }
|
23 | }
|
24 | ]
|
25 |
|
26 | constructor({pluginHandler}) {
|
27 | this.pluginHandler = pluginHandler;
|
28 | }
|
29 |
|
30 | addModel(model) {
|
31 | this.models.push(this._additionalNamespacePrefix(model))
|
32 | }
|
33 |
|
34 | delModel(namespace) {
|
35 | this.models = this.models.filter((model) => model.namespace !== namespace)
|
36 | }
|
37 |
|
38 | getCombineReducers() {
|
39 | return combineReducers(
|
40 | this._getModels().map(
|
41 | ({namespace, reducers = {}, initialState = {}}) =>
|
42 | ({[namespace]: handleActions(this._applyOnReducerEvent(Object.keys(reducers), reducers, {}), initialState)})
|
43 | ).reduce(this._reduce, {})
|
44 | )
|
45 | }
|
46 |
|
47 | getCombineEffects() {
|
48 | const effects = this._getModels().map(({effects}) => effects).reduce((prev, next) => ({...prev, ...next}), {})
|
49 | return this._getRootSaga.bind(this, effects)
|
50 | }
|
51 |
|
52 | _getModels() {
|
53 |
|
54 | const defaultModels = this.defaultModels.map(model => this._additionalNamespacePrefix(model))
|
55 |
|
56 | const pluginModels = this.pluginHandler.plugins.filter(plugin => plugin.reducers).map(plugin => ({
|
57 | namespace: plugin.namespace,
|
58 | initialState: plugin.initialState || {},
|
59 | reducers: plugin.reducers
|
60 | })).map(pluginModel => this._additionalNamespacePrefix(pluginModel))
|
61 |
|
62 | return defaultModels
|
63 | .concat(this.models)
|
64 | .concat(pluginModels)
|
65 | }
|
66 |
|
67 | * _mapGenerateSaga(effects) {
|
68 | const effectKs = Object.keys(effects);
|
69 | for (const name of effectKs) {
|
70 | const effect = this._packEffect(name, effects[name]);
|
71 | const watcher = this._getWatcher(effect);
|
72 | yield reduxSagaEffects.fork(watcher)
|
73 | }
|
74 | }
|
75 |
|
76 | * _getRootSaga(effects) {
|
77 | const rootTask = yield reduxSagaEffects.fork(this._mapGenerateSaga.bind(this, effects))
|
78 | yield reduxSagaEffects.fork(function* () {
|
79 | yield reduxSagaEffects.take(`@@dura/cancel`)
|
80 | yield reduxSagaEffects.cancel(rootTask)
|
81 | })
|
82 | }
|
83 |
|
84 | _packEffect(name, effect) {
|
85 | const defaultType = 'takeEvery';
|
86 | const newEffect = {
|
87 | name: name,
|
88 | saga: undefined,
|
89 | type: defaultType,
|
90 | ms: 100
|
91 | }
|
92 | const onEffectEventFuns = this.pluginHandler.getOnEffectEventFun();
|
93 | if (!Array.isArray(effect)) {
|
94 | newEffect.saga = recursiveEnhanceFun(onEffectEventFuns, effect, name, reduxSagaEffects);
|
95 | } else {
|
96 | const [saga, conf] = effect;
|
97 | if (!typeof conf) {
|
98 | newEffect.type = defaultType;
|
99 | } else if (typeof conf === 'string') {
|
100 | newEffect.type = conf;
|
101 | } else if (typeof conf === 'object') {
|
102 | newEffect.type = conf?.type || defaultType;
|
103 | } else {
|
104 | newEffect.type = defaultType;
|
105 | }
|
106 | newEffect.saga = recursiveEnhanceFun(onEffectEventFuns, saga, name, reduxSagaEffects);
|
107 | }
|
108 | return newEffect;
|
109 | }
|
110 |
|
111 | _getWatcher(effect) {
|
112 | const {name, saga, type, ms} = effect;
|
113 | switch (type) {
|
114 | case 'takeLatest':
|
115 | return function* (...args) {
|
116 | yield reduxSagaEffects.takeLatest(name, saga, reduxSagaEffects, args);
|
117 | }
|
118 | case 'takeLeading':
|
119 | return function* (...args) {
|
120 | yield reduxSagaEffects.takeLeading(name, saga, reduxSagaEffects, args);
|
121 | }
|
122 | case 'throttle':
|
123 | return function* (...args) {
|
124 | yield reduxSagaEffects.throttle(ms, name, saga, reduxSagaEffects, args);
|
125 | }
|
126 | default:
|
127 | return function* (...args) {
|
128 | yield reduxSagaEffects.takeEvery(name, saga, reduxSagaEffects, args);
|
129 | }
|
130 | }
|
131 | }
|
132 |
|
133 | _applyOnReducerEvent(reducerKeys, reducers, nextReducers) {
|
134 | const first = reducerKeys.shift();
|
135 | if (first) {
|
136 | return this._applyOnReducerEvent(reducerKeys, reducers, {
|
137 | ...nextReducers,
|
138 | [first]: recursiveEnhanceFun(this.pluginHandler.getOnReducerEventFun(), reducers[first])
|
139 | })
|
140 | }
|
141 | return nextReducers
|
142 | }
|
143 |
|
144 | _additionalNamespacePrefix(model) {
|
145 | const {namespace, reducers = {}, effects = {}, initialState = {}} = model
|
146 | const [newReducers, newEffects] = [
|
147 | this._rename(namespace, reducers, 'reducers'),
|
148 | this._rename(namespace, effects, 'effects')
|
149 | ]
|
150 | return ({
|
151 | namespace, initialState, reducers: newReducers, effects: newEffects
|
152 | })
|
153 | }
|
154 |
|
155 | _reduce(prev, next) {
|
156 | return ({...prev, ...next})
|
157 | }
|
158 |
|
159 | _rename(namespace, argObj, type) {
|
160 | return Object.keys(argObj).map((key) => ({[`${namespace}/${type}/${key}`]: argObj[key]})).reduce(this._reduce, {})
|
161 | }
|
162 |
|
163 | }
|
164 |
|
165 | export default ModelHandler |
\ | No newline at end of file |