1
- import { useReducer , useEffect , useCallback , useRef } from 'react' ;
1
+ import { useReducer , useEffect , useCallback , useRef , useMemo } from 'react' ;
2
2
3
3
type CleanupFunction = ( ) => void ;
4
4
5
- export type EffectFunction < TState , TEvent > = (
5
+ export type EffectFunction <
6
+ TState ,
7
+ TEvent extends EventObject ,
8
+ TEffect extends EffectObject < TState , TEvent >
9
+ > = (
6
10
state : TState ,
7
- effect : EffectObject < TState , TEvent > ,
11
+ effect : TEffect ,
8
12
dispatch : React . Dispatch < TEvent >
9
13
) => CleanupFunction | void ;
10
14
11
- export interface EffectObject < TState , TEvent > {
15
+ export interface EffectObject < TState , TEvent extends EventObject > {
12
16
[ key : string ] : any ;
13
17
type : string ;
14
- exec ?: EffectFunction < TState , TEvent > ;
18
+ exec ?: EffectFunction < TState , TEvent , any > ;
15
19
}
16
20
17
21
export type Effect <
18
22
TState ,
19
- TEvent ,
23
+ TEvent extends EventObject ,
20
24
TEffect extends EffectObject < TState , TEvent >
21
- > = TEffect | EffectFunction < TState , TEvent > ;
25
+ > = TEffect | EffectFunction < TState , TEvent , TEffect > ;
22
26
23
27
type EntityTuple < TState , TEvent extends EventObject > = [
24
28
TState ,
@@ -81,14 +85,14 @@ export interface EffectReducerExec<
81
85
TEvent extends EventObject ,
82
86
TEffect extends EffectObject < TState , TEvent >
83
87
> {
84
- ( effect : TEffect | EffectFunction < TState , TEvent > ) : EffectEntity <
88
+ ( effect : TEffect | EffectFunction < TState , TEvent , TEffect > ) : EffectEntity <
85
89
TState ,
86
90
TEvent
87
91
> ;
88
92
stop : ( entity : EffectEntity < TState , TEvent > ) => void ;
89
93
replace : (
90
94
entity : EffectEntity < TState , TEvent > | undefined ,
91
- effect : TEffect | EffectFunction < TState , TEvent >
95
+ effect : TEffect | EffectFunction < TState , TEvent , TEffect >
92
96
) => EffectEntity < TState , TEvent > ;
93
97
}
94
98
@@ -110,18 +114,26 @@ interface FlushEvent {
110
114
count : number ;
111
115
}
112
116
113
- export function toEffect < TState , TEvent > (
114
- exec : EffectFunction < TState , TEvent >
117
+ export function toEffect < TState , TEvent extends EventObject > (
118
+ exec : EffectFunction < TState , TEvent , any >
115
119
) : Effect < TState , TEvent , any > {
116
120
return {
117
121
type : exec . name ,
118
122
exec,
119
123
} ;
120
124
}
121
125
122
- interface EffectsMap < TState , TEvent > {
123
- [ key : string ] : EffectFunction < TState , TEvent > ;
124
- }
126
+ export type EffectsMap <
127
+ TState ,
128
+ TEvent extends EventObject ,
129
+ TEffect extends EffectObject < TState , TEvent >
130
+ > = {
131
+ [ key in TEffect [ 'type' ] ] : EffectFunction <
132
+ TState ,
133
+ TEvent ,
134
+ TEffect & { type : key }
135
+ > ;
136
+ } ;
125
137
126
138
const toEventObject = < TEvent extends EventObject > (
127
139
event : TEvent [ 'type' ] | TEvent
@@ -138,26 +150,35 @@ const toEffectObject = <
138
150
TEvent extends EventObject ,
139
151
TEffect extends EffectObject < TState , TEvent >
140
152
> (
141
- effect : TEffect | EffectFunction < TState , TEvent > ,
142
- effectsMap ?: EffectsMap < TState , TEvent >
153
+ effect : TEffect | EffectFunction < TState , TEvent , TEffect > ,
154
+ effectsMap ?: EffectsMap < TState , TEvent , TEffect >
143
155
) : TEffect => {
144
156
const type = typeof effect === 'function' ? effect . name : effect . type ;
145
- const customExec = effectsMap ? effectsMap [ type ] : undefined ;
157
+ const customExec = effectsMap
158
+ ? effectsMap [ type as TEffect [ 'type' ] ]
159
+ : undefined ;
146
160
const exec =
147
161
customExec || ( typeof effect === 'function' ? effect : effect . exec ) ;
148
162
const other = typeof effect === 'function' ? { } : effect ;
149
163
150
164
return { ...other , type, exec } as TEffect ;
151
165
} ;
152
166
167
+ export type InitialEffectStateGetter <
168
+ TState ,
169
+ TEffect extends EffectObject < TState , any >
170
+ > = (
171
+ exec : ( effect : TEffect | EffectFunction < TState , any , TEffect > ) => void
172
+ ) => TState ;
173
+
153
174
export function useEffectReducer <
154
175
TState ,
155
176
TEvent extends EventObject ,
156
177
TEffect extends EffectObject < TState , TEvent > = EffectObject < TState , TEvent >
157
178
> (
158
179
effectReducer : EffectReducer < TState , TEvent , TEffect > ,
159
- initialState : TState ,
160
- effectsMap ?: EffectsMap < TState , TEvent >
180
+ initialState : TState | InitialEffectStateGetter < TState , TEffect > ,
181
+ effectsMap ?: EffectsMap < TState , TEvent , TEffect >
161
182
) : [ TState , React . Dispatch < TEvent | TEvent [ 'type' ] > ] {
162
183
const entitiesRef = useRef < Set < EffectEntity < TState , TEvent > > > ( new Set ( ) ) ;
163
184
const wrappedReducer = (
@@ -175,7 +196,9 @@ export function useEffectReducer<
175
196
return [ state , stateEffectTuples . slice ( event . count ) , nextEntitiesToStop ] ;
176
197
}
177
198
178
- const exec = ( effect : TEffect | EffectFunction < TState , TEvent > ) => {
199
+ const exec = (
200
+ effect : TEffect | EffectFunction < TState , TEvent , TEffect >
201
+ ) => {
179
202
const effectObject = toEffectObject ( effect , effectsMap ) ;
180
203
const effectEntity = createEffectEntity < TState , TEvent , TEffect > (
181
204
effectObject
@@ -191,7 +214,7 @@ export function useEffectReducer<
191
214
192
215
exec . replace = (
193
216
entity : EffectEntity < TState , TEvent > ,
194
- effect : TEffect | EffectFunction < TState , TEvent >
217
+ effect : TEffect | EffectFunction < TState , TEvent , TEffect >
195
218
) => {
196
219
if ( entity ) {
197
220
nextEntitiesToStop . push ( entity ) ;
@@ -216,10 +239,39 @@ export function useEffectReducer<
216
239
] ;
217
240
} ;
218
241
242
+ const initialStateAndEffects : AggregatedEffectsState <
243
+ TState ,
244
+ TEvent
245
+ > = useMemo ( ( ) => {
246
+ if ( typeof initialState === 'function' ) {
247
+ const initialEffectEntities : Array < EffectEntity < TState , TEvent > > = [ ] ;
248
+
249
+ const resolvedInitialState = ( initialState as InitialEffectStateGetter <
250
+ TState ,
251
+ TEffect
252
+ > ) ( effect => {
253
+ const effectObject = toEffectObject ( effect , effectsMap ) ;
254
+ const effectEntity = createEffectEntity < TState , TEvent , TEffect > (
255
+ effectObject
256
+ ) ;
257
+
258
+ initialEffectEntities . push ( effectEntity ) ;
259
+ } ) ;
260
+
261
+ return [
262
+ resolvedInitialState ,
263
+ [ [ resolvedInitialState , initialEffectEntities ] ] ,
264
+ [ ] ,
265
+ ] ;
266
+ }
267
+
268
+ return [ initialState , [ ] , [ ] ] ;
269
+ } , [ ] ) ;
270
+
219
271
const [
220
272
[ state , effectStateEntityTuples , entitiesToStop ] ,
221
273
dispatch ,
222
- ] = useReducer ( wrappedReducer , [ initialState , [ ] , [ ] ] ) ;
274
+ ] = useReducer ( wrappedReducer , initialStateAndEffects ) ;
223
275
224
276
const wrappedDispatch = useCallback ( ( event : TEvent | TEvent [ 'type' ] ) => {
225
277
dispatch ( toEventObject ( event ) ) ;
0 commit comments