1
- import { useState , useRef , useEffect } from 'react'
1
+ import { useState , useRef , useEffect , Dispatch } from 'react'
2
2
import produce from 'immer'
3
- import { ConfigReducer , ContextPropsModel , MapStateToProps , ModelConfig , ModelContextProps , ModelEffect , Subscriber } from '../types'
3
+ import {
4
+ ConfigReducer ,
5
+ ContextPropsModel ,
6
+ MapStateToProps ,
7
+ ModelConfig ,
8
+ ModelConfigEffect ,
9
+ ModelContextProps ,
10
+ StateSubscriber
11
+ } from '../types'
4
12
import { invariant } from '../utils/invariant'
5
13
import { isObject } from '../utils/type'
6
14
import { createProvider } from './createProvider'
7
15
import { Container } from './Container'
16
+ import { noop } from '../utils/func'
8
17
9
18
interface ModelOptions < C extends ModelConfig > {
10
19
storeName : string
@@ -20,7 +29,11 @@ export class Model<C extends ModelConfig> {
20
29
21
30
private model : ContextPropsModel < C >
22
31
private initialState : C [ 'state' ]
32
+
23
33
private container : Container < C [ 'state' ] >
34
+ private currentDispatcher : Dispatch < any > = noop
35
+ private isInternalUpdate = false
36
+
24
37
private useContext : ( ) => ModelContextProps
25
38
26
39
constructor ( private options : ModelOptions < C > ) {
@@ -40,12 +53,13 @@ export class Model<C extends ModelConfig> {
40
53
41
54
public useModel ( mapStateToProps : MapStateToProps < C [ 'state' ] > ) : any {
42
55
const [ , dispatcher ] = useState ( )
43
- const { subscribers } = this . container
44
56
const { model } = this . useContext ( )
45
57
46
- const subscriberRef = useRef < Subscriber < C [ 'state' ] > > ( )
58
+ this . currentDispatcher = dispatcher
59
+
60
+ const subscriberRef = useRef < StateSubscriber < C [ 'state' ] > > ( )
47
61
48
- // 组件初始化时注册监听函数,使用 useRef 保证每个组件只注册一次
62
+ // make sure only subscribe once
49
63
if ( ! subscriberRef . current ) {
50
64
const subscriber = {
51
65
dispatcher,
@@ -54,15 +68,14 @@ export class Model<C extends ModelConfig> {
54
68
}
55
69
56
70
subscriberRef . current = subscriber
57
- subscribers . push ( subscriber )
71
+ this . container . subscribe ( 'state' , subscriber )
58
72
}
59
73
60
- /* eslint-disable-next-line */
61
74
useEffect ( ( ) => {
62
75
return ( ) : void => {
63
- // 组件卸载时解绑监听函数
64
- const index = subscribers . indexOf ( subscriberRef . current ! )
65
- subscribers . splice ( index , 1 )
76
+ // unsubscribe when component unmount
77
+ this . container . unsubscribe ( 'state' , subscriberRef . current as StateSubscriber < C [ 'state' ] > )
78
+ this . container . unsubscribe ( 'effect' , dispatcher )
66
79
}
67
80
} , [ ] )
68
81
@@ -116,7 +129,7 @@ export class Model<C extends ModelConfig> {
116
129
Object . create ( null )
117
130
)
118
131
119
- // 如果用户没有定义 setValue 则内置该方法
132
+ // internal reducer setValue
120
133
if ( ! reducers . setValue ) {
121
134
reducers . setValue = ( key , value ) : void => {
122
135
const newState = this . produceState ( this . model . state , draft => {
@@ -128,7 +141,7 @@ export class Model<C extends ModelConfig> {
128
141
}
129
142
}
130
143
131
- // 如果用户没有定义 setValues 则内置该方法
144
+ // internal reducer setValues
132
145
if ( ! reducers . setValues ) {
133
146
reducers . setValues = ( partialState ) : void => {
134
147
const newState = this . produceState ( this . model . state , draft => {
@@ -143,7 +156,7 @@ export class Model<C extends ModelConfig> {
143
156
}
144
157
}
145
158
146
- // 如果用户没有定义 reset 则内置该方法
159
+ // internal reducer reset
147
160
if ( ! reducers . reset ) {
148
161
reducers . reset = ( key ) : void => {
149
162
const newState = this . produceState ( this . model . state , draft => {
@@ -168,12 +181,13 @@ export class Model<C extends ModelConfig> {
168
181
return Object . keys ( config . effects ) . reduce ( ( effects , name ) => {
169
182
const originEffect = config . effects [ name ]
170
183
171
- const effect : ModelEffect < typeof originEffect > = async ( ...payload : any ) : Promise < void > => {
184
+ const effect : ModelConfigEffect < typeof originEffect > = async ( ...payload : any ) : Promise < void > => {
172
185
try {
173
186
effect . identifier ++
187
+
188
+ this . isInternalUpdate = true
174
189
effect . loading = true
175
-
176
- this . container . notify ( null )
190
+ this . isInternalUpdate = false
177
191
178
192
const result = await originEffect ( ...payload )
179
193
return result
@@ -184,14 +198,37 @@ export class Model<C extends ModelConfig> {
184
198
185
199
/* istanbul ignore else */
186
200
if ( effect . identifier === 0 ) {
201
+ this . isInternalUpdate = true
187
202
effect . loading = false
188
- this . container . notify ( null )
203
+ this . isInternalUpdate = false
189
204
}
190
205
}
191
206
}
192
207
193
208
effect . loading = false
194
209
effect . identifier = 0
210
+
211
+ let value = false
212
+ const that = this
213
+
214
+ Object . defineProperty ( effect , 'loading' , {
215
+ configurable : false ,
216
+ enumerable : true ,
217
+
218
+ get ( ) {
219
+ that . container . subscribe ( 'effect' , that . currentDispatcher )
220
+ return value
221
+ } ,
222
+
223
+ set ( newValue ) {
224
+ // avoid modify effect loading out of internal
225
+ if ( newValue !== value && that . isInternalUpdate ) {
226
+ value = newValue
227
+ that . container . notify ( )
228
+ }
229
+ }
230
+ } )
231
+
195
232
effects [ name ] = effect
196
233
197
234
return effects
0 commit comments