Skip to content

Commit 92b475a

Browse files
committed
Created listenerApi.cancel()
See #3683
1 parent ad8d983 commit 92b475a

File tree

3 files changed

+45
-8
lines changed

3 files changed

+45
-8
lines changed

packages/toolkit/src/listenerMiddleware/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,13 @@ export function createListenerMiddleware<
419419
}
420420
})
421421
},
422+
cancel: () => {
423+
abortControllerWithReason(
424+
internalTaskController,
425+
listenerCancelled
426+
)
427+
entry.pending.delete(internalTaskController)
428+
},
422429
})
423430
)
424431
)

packages/toolkit/src/listenerMiddleware/tests/listenerMiddleware.test.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const middlewareApi = {
4545
unsubscribe: expect.any(Function),
4646
subscribe: expect.any(Function),
4747
cancelActiveListeners: expect.any(Function),
48+
cancel: expect.any(Function),
4849
}
4950

5051
const noop = () => {}
@@ -184,7 +185,7 @@ describe('createListenerMiddleware', () => {
184185
middleware: (gDM) => gDM().prepend(listenerMiddleware.middleware),
185186
})
186187

187-
let foundExtra: number | null = null
188+
let foundExtra: number | null = null
188189

189190
const typedAddListener =
190191
listenerMiddleware.startListening as TypedStartListening<
@@ -645,7 +646,32 @@ describe('createListenerMiddleware', () => {
645646
expect(await deferredCompletedSignalReason).toBe(listenerCompleted)
646647
})
647648

648-
test('"can unsubscribe via middleware api', () => {
649+
test('can self-cancel via middleware api', async () => {
650+
const notifyDeferred = createAction<Deferred<string>>('notify-deferred')
651+
652+
startListening({
653+
actionCreator: notifyDeferred,
654+
effect: async ({ payload }, { signal, cancel, delay }) => {
655+
signal.addEventListener(
656+
'abort',
657+
() => {
658+
payload.resolve((signal as AbortSignalWithReason<string>).reason)
659+
},
660+
{ once: true }
661+
)
662+
663+
cancel()
664+
},
665+
})
666+
667+
const deferredCancelledSignalReason = store.dispatch(
668+
notifyDeferred(deferred<string>())
669+
).payload
670+
671+
expect(await deferredCancelledSignalReason).toBe(listenerCancelled)
672+
})
673+
674+
test('can unsubscribe via middleware api', () => {
649675
const effect = jest.fn(
650676
(action: TestAction1, api: ListenerEffectAPI<any, any>) => {
651677
if (action.payload === 'b') {
@@ -1126,7 +1152,7 @@ describe('createListenerMiddleware', () => {
11261152
expect(takeResult).toEqual([increment(), stateCurrent, stateBefore])
11271153
})
11281154

1129-
test("take resolves to `[A, CurrentState, PreviousState] | null` if a possibly undefined timeout parameter is provided", async () => {
1155+
test('take resolves to `[A, CurrentState, PreviousState] | null` if a possibly undefined timeout parameter is provided', async () => {
11301156
const store = configureStore({
11311157
reducer: counterSlice.reducer,
11321158
middleware: (gDM) => gDM().prepend(middleware),
@@ -1160,7 +1186,7 @@ describe('createListenerMiddleware', () => {
11601186
store.dispatch(increment())
11611187

11621188
await delay(25)
1163-
expect(done).toBe(true);
1189+
expect(done).toBe(true)
11641190
})
11651191

11661192
test('condition method resolves promise when the predicate succeeds', async () => {

packages/toolkit/src/listenerMiddleware/types.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export interface ForkOptions {
137137
* If true, causes the parent task to not be marked as complete until
138138
* all autoJoined forks have completed or failed.
139139
*/
140-
autoJoin: boolean;
140+
autoJoin: boolean
141141
}
142142

143143
/** @public */
@@ -186,9 +186,9 @@ export interface ListenerEffectAPI<
186186
* rejects if the listener has been cancelled or is completed.
187187
*
188188
* The return value is `true` if the predicate succeeds or `false` if a timeout is provided and expires first.
189-
*
189+
*
190190
* ### Example
191-
*
191+
*
192192
* ```ts
193193
* const updateBy = createAction<number>('counter/updateBy');
194194
*
@@ -210,7 +210,7 @@ export interface ListenerEffectAPI<
210210
*
211211
* The return value is the `[action, currentState, previousState]` combination that the predicate saw as arguments.
212212
*
213-
* The promise resolves to null if a timeout is provided and expires first,
213+
* The promise resolves to null if a timeout is provided and expires first,
214214
*
215215
* ### Example
216216
*
@@ -233,6 +233,10 @@ export interface ListenerEffectAPI<
233233
* Cancels all other running instances of this same listener except for the one that made this call.
234234
*/
235235
cancelActiveListeners: () => void
236+
/**
237+
* Cancels the instance of this listener that made this call.
238+
*/
239+
cancel: () => void
236240
/**
237241
* An abort signal whose `aborted` property is set to `true`
238242
* if the listener execution is either aborted or completed.

0 commit comments

Comments
 (0)