Skip to content

Commit e9230e8

Browse files
committed
feat(alm): add clearListenerMiddlewareAction reduxjs#1952
see: reduxjs#1952 (comment)
1 parent 4a97391 commit e9230e8

File tree

2 files changed

+108
-47
lines changed

2 files changed

+108
-47
lines changed

packages/action-listener-middleware/src/index.ts

+17-2
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,9 @@ export const createListenerEntry: TypedCreateListenerEntry<unknown> = (
198198
return entry
199199
}
200200

201-
const createClearAllListeners = (listenerMap: Map<string, ListenerEntry>) => {
201+
const createClearListenerMiddleware = (
202+
listenerMap: Map<string, ListenerEntry>
203+
) => {
202204
return () => {
203205
listenerMap.forEach((entry) => {
204206
entry.pending.forEach((controller) => {
@@ -250,6 +252,11 @@ export const addListenerAction = createAction(
250252
}
251253
) as TypedAddListenerAction<unknown>
252254

255+
/**
256+
* @alpha
257+
*/
258+
export const clearListenerMiddlewareAction = createAction(`${alm}/clear`)
259+
253260
/**
254261
* @alpha
255262
*/
@@ -422,6 +429,8 @@ export function createActionListenerMiddleware<
422429
}
423430
}
424431

432+
const clearListenerMiddleware = createClearListenerMiddleware(listenerMap)
433+
425434
const middleware: Middleware<
426435
{
427436
(action: Action<`${typeof alm}/add`>): Unsubscribe
@@ -440,6 +449,12 @@ export function createActionListenerMiddleware<
440449

441450
return insertEntry(entry)
442451
}
452+
453+
if (clearListenerMiddlewareAction.match(action)) {
454+
clearListenerMiddleware()
455+
return
456+
}
457+
443458
if (removeListenerAction.match(action)) {
444459
removeListener(action.payload.type, action.payload.listener)
445460
return
@@ -501,7 +516,7 @@ export function createActionListenerMiddleware<
501516
{
502517
addListener,
503518
removeListener,
504-
clear: createClearAllListeners(listenerMap),
519+
clear: clearListenerMiddleware,
505520
addListenerAction: addListenerAction as TypedAddListenerAction<S>,
506521
},
507522
{} as WithMiddlewareType<typeof middleware>

packages/action-listener-middleware/src/tests/listenerMiddleware.test.ts

+91-45
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
addListenerAction,
1515
removeListenerAction,
1616
TaskAbortError,
17+
clearListenerMiddlewareAction,
1718
} from '../index'
1819

1920
import type {
@@ -492,62 +493,107 @@ describe('createActionListenerMiddleware', () => {
492493
})
493494
})
494495

495-
test('clear() cancels running listeners and removes all subscriptions', async () => {
496-
const listener1Test = deferred()
497-
498-
let listener1Calls = 0
499-
let listener2Calls = 0
500-
501-
middleware.addListener({
502-
actionCreator: testAction1,
503-
async listener(_, listenerApi) {
504-
listener1Calls++
505-
listenerApi.signal.addEventListener(
506-
'abort',
507-
() => listener1Test.resolve(listener1Calls),
508-
{ once: true }
509-
)
510-
await listenerApi.condition(() => true)
511-
listener1Test.reject(new Error('unreachable: listener1Test'))
512-
},
513-
})
496+
describe('clear listeners', () => {
497+
test('dispatch(clearListenerAction()) cancels running listeners and removes all subscriptions', async () => {
498+
const listener1Test = deferred()
499+
let listener1Calls = 0
500+
let listener2Calls = 0
501+
let listener3Calls = 0
514502

515-
middleware.addListener({
516-
actionCreator: testAction2,
517-
listener() {
518-
listener2Calls++
519-
},
503+
middleware.addListener({
504+
actionCreator: testAction1,
505+
async listener(_, listenerApi) {
506+
listener1Calls++
507+
listenerApi.signal.addEventListener(
508+
'abort',
509+
() => listener1Test.resolve(listener1Calls),
510+
{ once: true }
511+
)
512+
await listenerApi.condition(() => true)
513+
listener1Test.reject(new Error('unreachable: listener1Test'))
514+
},
515+
})
516+
517+
middleware.addListener({
518+
actionCreator: clearListenerMiddlewareAction,
519+
listener() {
520+
listener2Calls++
521+
},
522+
})
523+
524+
middleware.addListener({
525+
predicate: () => true,
526+
listener() {
527+
listener3Calls++
528+
},
529+
})
530+
531+
store.dispatch(testAction1('a'))
532+
store.dispatch(clearListenerMiddlewareAction())
533+
store.dispatch(testAction1('b'))
534+
expect(await listener1Test).toBe(1)
535+
expect(listener1Calls).toBe(1)
536+
expect(listener3Calls).toBe(1)
537+
expect(listener2Calls).toBe(0)
520538
})
521539

522-
store.dispatch(testAction1('a'))
540+
test('clear() cancels running listeners and removes all subscriptions', async () => {
541+
const listener1Test = deferred()
542+
543+
let listener1Calls = 0
544+
let listener2Calls = 0
523545

524-
middleware.clear()
525-
store.dispatch(testAction1('b'))
526-
store.dispatch(testAction2('c'))
546+
middleware.addListener({
547+
actionCreator: testAction1,
548+
async listener(_, listenerApi) {
549+
listener1Calls++
550+
listenerApi.signal.addEventListener(
551+
'abort',
552+
() => listener1Test.resolve(listener1Calls),
553+
{ once: true }
554+
)
555+
await listenerApi.condition(() => true)
556+
listener1Test.reject(new Error('unreachable: listener1Test'))
557+
},
558+
})
527559

528-
expect(listener2Calls).toBe(0)
529-
expect(await listener1Test).toBe(1)
530-
})
560+
middleware.addListener({
561+
actionCreator: testAction2,
562+
listener() {
563+
listener2Calls++
564+
},
565+
})
531566

532-
test('clear() cancels all running forked tasks', async () => {
533-
const fork1Test = deferred()
567+
store.dispatch(testAction1('a'))
534568

535-
middleware.addListener({
536-
actionCreator: testAction1,
537-
async listener(_, { fork }) {
538-
const taskResult = await fork(() => {
539-
return 3
540-
}).result
541-
fork1Test.resolve(taskResult)
542-
},
569+
middleware.clear()
570+
store.dispatch(testAction1('b'))
571+
store.dispatch(testAction2('c'))
572+
573+
expect(listener2Calls).toBe(0)
574+
expect(await listener1Test).toBe(1)
543575
})
544576

545-
store.dispatch(testAction1('a'))
577+
test('clear() cancels all running forked tasks', async () => {
578+
const fork1Test = deferred()
546579

547-
middleware.clear()
548-
store.dispatch(testAction1('b'))
580+
middleware.addListener({
581+
actionCreator: testAction1,
582+
async listener(_, { fork }) {
583+
const taskResult = await fork(() => {
584+
return 3
585+
}).result
586+
fork1Test.resolve(taskResult)
587+
},
588+
})
589+
590+
store.dispatch(testAction1('a'))
549591

550-
expect(await fork1Test).toHaveProperty('status', 'cancelled')
592+
middleware.clear()
593+
store.dispatch(testAction1('b'))
594+
595+
expect(await fork1Test).toHaveProperty('status', 'cancelled')
596+
})
551597
})
552598

553599
describe('Listener API', () => {

0 commit comments

Comments
 (0)