UNPKG

5.48 kBJavaScriptView Raw
1import {combineReducers} from 'redux'
2import {handleActions} from 'redux-actions'
3import {recursiveEnhanceFun} from 'dura-util'
4import * as reduxSagaEffects from "redux-saga/effects";
5
6class 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
165export default ModelHandler
\No newline at end of file