@@ -9,25 +9,25 @@ import type {
9
9
import { createAction , nanoid } from '@reduxjs/toolkit'
10
10
11
11
import type {
12
- ActionListenerMiddleware ,
12
+ ListenerMiddleware ,
13
+ ListenerMiddlewareInstance ,
13
14
AddListenerOverloads ,
14
- AnyActionListenerPredicate ,
15
+ AnyListenerPredicate ,
15
16
CreateListenerMiddlewareOptions ,
16
17
TypedActionCreator ,
18
+ TypedStartListening ,
17
19
TypedAddListener ,
18
- TypedAddListenerAction ,
19
20
TypedCreateListenerEntry ,
20
21
FallbackAddListenerOptions ,
21
22
ListenerEntry ,
22
23
ListenerErrorHandler ,
23
24
Unsubscribe ,
24
- WithMiddlewareType ,
25
25
TakePattern ,
26
26
ListenerErrorInfo ,
27
27
ForkedTaskExecutor ,
28
28
ForkedTask ,
29
- TypedRemoveListenerAction ,
30
29
TypedRemoveListener ,
30
+ TypedStopListening ,
31
31
} from './types'
32
32
import { assertFunction , catchRejection } from './utils'
33
33
import { TaskAbortError } from './exceptions'
@@ -40,16 +40,15 @@ import {
40
40
} from './task'
41
41
export { TaskAbortError } from './exceptions'
42
42
export type {
43
- ActionListener ,
44
- ActionListenerMiddleware ,
45
- ActionListenerMiddlewareAPI ,
46
- ActionListenerOptions ,
43
+ ListenerEffect ,
44
+ ListenerMiddleware ,
45
+ ListenerEffectAPI ,
47
46
CreateListenerMiddlewareOptions ,
48
47
ListenerErrorHandler ,
48
+ TypedStartListening ,
49
49
TypedAddListener ,
50
- TypedAddListenerAction ,
50
+ TypedStopListening ,
51
51
TypedRemoveListener ,
52
- TypedRemoveListenerAction ,
53
52
Unsubscribe ,
54
53
ForkedTaskExecutor ,
55
54
ForkedTask ,
@@ -69,7 +68,7 @@ const { assign } = Object
69
68
*/
70
69
const INTERNAL_NIL_TOKEN = { } as const
71
70
72
- const alm = 'actionListenerMiddleware ' as const
71
+ const alm = 'listenerMiddleware ' as const
73
72
74
73
const createFork = ( parentAbortSignal : AbortSignal ) => {
75
74
return < T > ( taskExecutor : ForkedTaskExecutor < T > ) : ForkedTask < T > => {
@@ -100,17 +99,17 @@ const createFork = (parentAbortSignal: AbortSignal) => {
100
99
}
101
100
102
101
const createTakePattern = < S > (
103
- addListener : AddListenerOverloads < Unsubscribe , S , Dispatch < AnyAction > > ,
102
+ startListening : AddListenerOverloads < Unsubscribe , S , Dispatch < AnyAction > > ,
104
103
signal : AbortSignal
105
104
) : TakePattern < S > => {
106
105
/**
107
- * A function that takes an ActionListenerPredicate and an optional timeout,
106
+ * A function that takes a ListenerPredicate and an optional timeout,
108
107
* and resolves when either the predicate returns `true` based on an action
109
108
* state combination or when the timeout expires.
110
109
* If the parent listener is canceled while waiting, this will throw a
111
110
* TaskAbortError.
112
111
*/
113
- const take = async < P extends AnyActionListenerPredicate < S > > (
112
+ const take = async < P extends AnyListenerPredicate < S > > (
114
113
predicate : P ,
115
114
timeout : number | undefined
116
115
) => {
@@ -121,9 +120,9 @@ const createTakePattern = <S>(
121
120
122
121
const tuplePromise = new Promise < [ AnyAction , S , S ] > ( ( resolve ) => {
123
122
// Inside the Promise, we synchronously add the listener.
124
- unsubscribe = addListener ( {
123
+ unsubscribe = startListening ( {
125
124
predicate : predicate as any ,
126
- listener : ( action , listenerApi ) : void => {
125
+ effect : ( action , listenerApi ) : void => {
127
126
// One-shot listener that cleans up as soon as the predicate passes
128
127
listenerApi . unsubscribe ( )
129
128
// Resolve the promise with the same arguments the predicate saw
@@ -158,14 +157,12 @@ const createTakePattern = <S>(
158
157
}
159
158
}
160
159
161
- return ( (
162
- predicate : AnyActionListenerPredicate < S > ,
163
- timeout : number | undefined
164
- ) => catchRejection ( take ( predicate , timeout ) ) ) as TakePattern < S >
160
+ return ( ( predicate : AnyListenerPredicate < S > , timeout : number | undefined ) =>
161
+ catchRejection ( take ( predicate , timeout ) ) ) as TakePattern < S >
165
162
}
166
163
167
164
const getListenerEntryPropsFrom = ( options : FallbackAddListenerOptions ) => {
168
- let { type, actionCreator, matcher, predicate, listener } = options
165
+ let { type, actionCreator, matcher, predicate, effect } = options
169
166
170
167
if ( type ) {
171
168
predicate = createAction ( type ) . match
@@ -182,21 +179,21 @@ const getListenerEntryPropsFrom = (options: FallbackAddListenerOptions) => {
182
179
)
183
180
}
184
181
185
- assertFunction ( listener , 'options.listener' )
182
+ assertFunction ( effect , 'options.listener' )
186
183
187
- return { predicate, type, listener }
184
+ return { predicate, type, effect }
188
185
}
189
186
190
187
/** Accepts the possible options for creating a listener, and returns a formatted listener entry */
191
188
export const createListenerEntry : TypedCreateListenerEntry < unknown > = (
192
189
options : FallbackAddListenerOptions
193
190
) => {
194
- const { type, predicate, listener } = getListenerEntryPropsFrom ( options )
191
+ const { type, predicate, effect } = getListenerEntryPropsFrom ( options )
195
192
196
193
const id = nanoid ( )
197
194
const entry : ListenerEntry < unknown > = {
198
195
id,
199
- listener ,
196
+ effect ,
200
197
type,
201
198
predicate,
202
199
pending : new Set < AbortController > ( ) ,
@@ -248,21 +245,21 @@ const safelyNotifyError = (
248
245
/**
249
246
* @alpha
250
247
*/
251
- export const addListenerAction = createAction (
248
+ export const addListener = createAction (
252
249
`${ alm } /add`
253
- ) as TypedAddListenerAction < unknown >
250
+ ) as TypedAddListener < unknown >
254
251
255
252
/**
256
253
* @alpha
257
254
*/
258
- export const clearListenerMiddlewareAction = createAction ( `${ alm } /clear ` )
255
+ export const removeAllListeners = createAction ( `${ alm } /removeAll ` )
259
256
260
257
/**
261
258
* @alpha
262
259
*/
263
- export const removeListenerAction = createAction (
260
+ export const removeListener = createAction (
264
261
`${ alm } /remove`
265
- ) as TypedRemoveListenerAction < unknown >
262
+ ) as TypedRemoveListener < unknown >
266
263
267
264
const defaultErrorHandler : ListenerErrorHandler = ( ...args : unknown [ ] ) => {
268
265
console . error ( `${ alm } /error` , ...args )
@@ -271,9 +268,8 @@ const defaultErrorHandler: ListenerErrorHandler = (...args: unknown[]) => {
271
268
/**
272
269
* @alpha
273
270
*/
274
- export function createActionListenerMiddleware <
271
+ export function createListenerMiddleware <
275
272
S = unknown ,
276
- // TODO Carry through the thunk extra arg somehow?
277
273
D extends Dispatch < AnyAction > = ThunkDispatch < S , unknown , AnyAction > ,
278
274
ExtraArgument = unknown
279
275
> ( middlewareOptions : CreateListenerMiddlewareOptions < ExtraArgument > = { } ) {
@@ -301,9 +297,9 @@ export function createActionListenerMiddleware<
301
297
return undefined
302
298
}
303
299
304
- const addListener = ( options : FallbackAddListenerOptions ) => {
300
+ const startListening = ( options : FallbackAddListenerOptions ) => {
305
301
let entry = findListenerEntry (
306
- ( existingEntry ) => existingEntry . listener === options . listener
302
+ ( existingEntry ) => existingEntry . effect === options . effect
307
303
)
308
304
309
305
if ( ! entry ) {
@@ -313,16 +309,16 @@ export function createActionListenerMiddleware<
313
309
return insertEntry ( entry )
314
310
}
315
311
316
- const removeListener = ( options : FallbackAddListenerOptions ) : boolean => {
317
- const { type, listener , predicate } = getListenerEntryPropsFrom ( options )
312
+ const stopListening = ( options : FallbackAddListenerOptions ) : boolean => {
313
+ const { type, effect , predicate } = getListenerEntryPropsFrom ( options )
318
314
319
315
const entry = findListenerEntry ( ( entry ) => {
320
316
const matchPredicateOrType =
321
317
typeof type === 'string'
322
318
? entry . type === type
323
319
: entry . predicate === predicate
324
320
325
- return matchPredicateOrType && entry . listener === listener
321
+ return matchPredicateOrType && entry . effect === effect
326
322
} )
327
323
328
324
entry ?. unsubscribe ( )
@@ -337,18 +333,21 @@ export function createActionListenerMiddleware<
337
333
getOriginalState : ( ) => S
338
334
) => {
339
335
const internalTaskController = new AbortController ( )
340
- const take = createTakePattern ( addListener , internalTaskController . signal )
336
+ const take = createTakePattern (
337
+ startListening ,
338
+ internalTaskController . signal
339
+ )
341
340
342
341
try {
343
342
entry . pending . add ( internalTaskController )
344
343
await Promise . resolve (
345
- entry . listener (
344
+ entry . effect (
346
345
action ,
347
346
// Use assign() rather than ... to avoid extra helper functions added to bundle
348
347
assign ( { } , api , {
349
348
getOriginalState,
350
349
condition : (
351
- predicate : AnyActionListenerPredicate < any > ,
350
+ predicate : AnyListenerPredicate < any > ,
352
351
timeout ?: number
353
352
) => take ( predicate , timeout ) . then ( Boolean ) ,
354
353
take,
@@ -375,7 +374,7 @@ export function createActionListenerMiddleware<
375
374
} catch ( listenerError ) {
376
375
if ( ! ( listenerError instanceof TaskAbortError ) ) {
377
376
safelyNotifyError ( onError , listenerError , {
378
- raisedBy : 'listener ' ,
377
+ raisedBy : 'effect ' ,
379
378
} )
380
379
}
381
380
} finally {
@@ -386,84 +385,76 @@ export function createActionListenerMiddleware<
386
385
387
386
const clearListenerMiddleware = createClearListenerMiddleware ( listenerMap )
388
387
389
- const middleware : Middleware <
390
- {
391
- ( action : Action < `${typeof alm } /add`> ) : Unsubscribe
392
- } ,
393
- S ,
394
- D
395
- > = ( api ) => ( next ) => ( action ) => {
396
- if ( addListenerAction . match ( action ) ) {
397
- return addListener ( action . payload )
398
- }
388
+ const middleware : ListenerMiddleware < S , D , ExtraArgument > =
389
+ ( api ) => ( next ) => ( action ) => {
390
+ if ( addListener . match ( action ) ) {
391
+ return startListening ( action . payload )
392
+ }
399
393
400
- if ( clearListenerMiddlewareAction . match ( action ) ) {
401
- clearListenerMiddleware ( )
402
- return
403
- }
394
+ if ( removeAllListeners . match ( action ) ) {
395
+ clearListenerMiddleware ( )
396
+ return
397
+ }
404
398
405
- if ( removeListenerAction . match ( action ) ) {
406
- return removeListener ( action . payload )
407
- }
399
+ if ( removeListener . match ( action ) ) {
400
+ return stopListening ( action . payload )
401
+ }
408
402
409
- // Need to get this state _before_ the reducer processes the action
410
- let originalState : S | typeof INTERNAL_NIL_TOKEN = api . getState ( )
403
+ // Need to get this state _before_ the reducer processes the action
404
+ let originalState : S | typeof INTERNAL_NIL_TOKEN = api . getState ( )
411
405
412
- // `getOriginalState` can only be called synchronously.
413
- // @see https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1932820
414
- const getOriginalState = ( ) : S => {
415
- if ( originalState === INTERNAL_NIL_TOKEN ) {
416
- throw new Error (
417
- `${ alm } : getOriginalState can only be called synchronously`
418
- )
406
+ // `getOriginalState` can only be called synchronously.
407
+ // @see https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1932820
408
+ const getOriginalState = ( ) : S => {
409
+ if ( originalState === INTERNAL_NIL_TOKEN ) {
410
+ throw new Error (
411
+ `${ alm } : getOriginalState can only be called synchronously`
412
+ )
413
+ }
414
+
415
+ return originalState as S
419
416
}
420
417
421
- return originalState as S
422
- }
418
+ let result : unknown
423
419
424
- let result : unknown
420
+ try {
421
+ // Actually forward the action to the reducer before we handle listeners
422
+ result = next ( action )
425
423
426
- try {
427
- // Actually forward the action to the reducer before we handle listeners
428
- result = next ( action )
429
-
430
- if ( listenerMap . size > 0 ) {
431
- let currentState = api . getState ( )
432
- for ( let entry of listenerMap . values ( ) ) {
433
- let runListener = false
434
-
435
- try {
436
- runListener = entry . predicate ( action , currentState , originalState )
437
- } catch ( predicateError ) {
438
- runListener = false
439
-
440
- safelyNotifyError ( onError , predicateError , {
441
- raisedBy : 'predicate' ,
442
- } )
443
- }
424
+ if ( listenerMap . size > 0 ) {
425
+ let currentState = api . getState ( )
426
+ for ( let entry of listenerMap . values ( ) ) {
427
+ let runListener = false
444
428
445
- if ( ! runListener ) {
446
- continue
447
- }
429
+ try {
430
+ runListener = entry . predicate ( action , currentState , originalState )
431
+ } catch ( predicateError ) {
432
+ runListener = false
448
433
449
- notifyListener ( entry , action , api , getOriginalState )
434
+ safelyNotifyError ( onError , predicateError , {
435
+ raisedBy : 'predicate' ,
436
+ } )
437
+ }
438
+
439
+ if ( ! runListener ) {
440
+ continue
441
+ }
442
+
443
+ notifyListener ( entry , action , api , getOriginalState )
444
+ }
450
445
}
446
+ } finally {
447
+ // Remove `originalState` store from this scope.
448
+ originalState = INTERNAL_NIL_TOKEN
451
449
}
452
- } finally {
453
- // Remove `originalState` store from this scope.
454
- originalState = INTERNAL_NIL_TOKEN
455
- }
456
450
457
- return result
458
- }
451
+ return result
452
+ }
459
453
460
- return assign (
454
+ return {
461
455
middleware,
462
- {
463
- addListener : addListener as TypedAddListener < S , D > ,
464
- removeListener : removeListener as TypedRemoveListener < S , D > ,
465
- clearListeners : clearListenerMiddleware ,
466
- } ,
467
- { } as WithMiddlewareType < typeof middleware >
468
- )
456
+ startListening,
457
+ stopListening,
458
+ clearListeners : clearListenerMiddleware ,
459
+ } as ListenerMiddlewareInstance < S , D , ExtraArgument >
469
460
}
0 commit comments