Skip to content

Commit

Permalink
feat(alm): add clearListenerMiddlewareAction #1952
Browse files Browse the repository at this point in the history
  • Loading branch information
FaberVitale committed Jan 22, 2022
1 parent 4a97391 commit e9230e8
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 47 deletions.
19 changes: 17 additions & 2 deletions packages/action-listener-middleware/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ export const createListenerEntry: TypedCreateListenerEntry<unknown> = (
return entry
}

const createClearAllListeners = (listenerMap: Map<string, ListenerEntry>) => {
const createClearListenerMiddleware = (
listenerMap: Map<string, ListenerEntry>
) => {
return () => {
listenerMap.forEach((entry) => {
entry.pending.forEach((controller) => {
Expand Down Expand Up @@ -250,6 +252,11 @@ export const addListenerAction = createAction(
}
) as TypedAddListenerAction<unknown>

/**
* @alpha
*/
export const clearListenerMiddlewareAction = createAction(`${alm}/clear`)

/**
* @alpha
*/
Expand Down Expand Up @@ -422,6 +429,8 @@ export function createActionListenerMiddleware<
}
}

const clearListenerMiddleware = createClearListenerMiddleware(listenerMap)

const middleware: Middleware<
{
(action: Action<`${typeof alm}/add`>): Unsubscribe
Expand All @@ -440,6 +449,12 @@ export function createActionListenerMiddleware<

return insertEntry(entry)
}

if (clearListenerMiddlewareAction.match(action)) {
clearListenerMiddleware()
return
}

if (removeListenerAction.match(action)) {
removeListener(action.payload.type, action.payload.listener)
return
Expand Down Expand Up @@ -501,7 +516,7 @@ export function createActionListenerMiddleware<
{
addListener,
removeListener,
clear: createClearAllListeners(listenerMap),
clear: clearListenerMiddleware,
addListenerAction: addListenerAction as TypedAddListenerAction<S>,
},
{} as WithMiddlewareType<typeof middleware>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
addListenerAction,
removeListenerAction,
TaskAbortError,
clearListenerMiddlewareAction,
} from '../index'

import type {
Expand Down Expand Up @@ -492,62 +493,107 @@ describe('createActionListenerMiddleware', () => {
})
})

test('clear() cancels running listeners and removes all subscriptions', async () => {
const listener1Test = deferred()

let listener1Calls = 0
let listener2Calls = 0

middleware.addListener({
actionCreator: testAction1,
async listener(_, listenerApi) {
listener1Calls++
listenerApi.signal.addEventListener(
'abort',
() => listener1Test.resolve(listener1Calls),
{ once: true }
)
await listenerApi.condition(() => true)
listener1Test.reject(new Error('unreachable: listener1Test'))
},
})
describe('clear listeners', () => {
test('dispatch(clearListenerAction()) cancels running listeners and removes all subscriptions', async () => {
const listener1Test = deferred()
let listener1Calls = 0
let listener2Calls = 0
let listener3Calls = 0

middleware.addListener({
actionCreator: testAction2,
listener() {
listener2Calls++
},
middleware.addListener({
actionCreator: testAction1,
async listener(_, listenerApi) {
listener1Calls++
listenerApi.signal.addEventListener(
'abort',
() => listener1Test.resolve(listener1Calls),
{ once: true }
)
await listenerApi.condition(() => true)
listener1Test.reject(new Error('unreachable: listener1Test'))
},
})

middleware.addListener({
actionCreator: clearListenerMiddlewareAction,
listener() {
listener2Calls++
},
})

middleware.addListener({
predicate: () => true,
listener() {
listener3Calls++
},
})

store.dispatch(testAction1('a'))
store.dispatch(clearListenerMiddlewareAction())
store.dispatch(testAction1('b'))
expect(await listener1Test).toBe(1)
expect(listener1Calls).toBe(1)
expect(listener3Calls).toBe(1)
expect(listener2Calls).toBe(0)
})

store.dispatch(testAction1('a'))
test('clear() cancels running listeners and removes all subscriptions', async () => {
const listener1Test = deferred()

let listener1Calls = 0
let listener2Calls = 0

middleware.clear()
store.dispatch(testAction1('b'))
store.dispatch(testAction2('c'))
middleware.addListener({
actionCreator: testAction1,
async listener(_, listenerApi) {
listener1Calls++
listenerApi.signal.addEventListener(
'abort',
() => listener1Test.resolve(listener1Calls),
{ once: true }
)
await listenerApi.condition(() => true)
listener1Test.reject(new Error('unreachable: listener1Test'))
},
})

expect(listener2Calls).toBe(0)
expect(await listener1Test).toBe(1)
})
middleware.addListener({
actionCreator: testAction2,
listener() {
listener2Calls++
},
})

test('clear() cancels all running forked tasks', async () => {
const fork1Test = deferred()
store.dispatch(testAction1('a'))

middleware.addListener({
actionCreator: testAction1,
async listener(_, { fork }) {
const taskResult = await fork(() => {
return 3
}).result
fork1Test.resolve(taskResult)
},
middleware.clear()
store.dispatch(testAction1('b'))
store.dispatch(testAction2('c'))

expect(listener2Calls).toBe(0)
expect(await listener1Test).toBe(1)
})

store.dispatch(testAction1('a'))
test('clear() cancels all running forked tasks', async () => {
const fork1Test = deferred()

middleware.clear()
store.dispatch(testAction1('b'))
middleware.addListener({
actionCreator: testAction1,
async listener(_, { fork }) {
const taskResult = await fork(() => {
return 3
}).result
fork1Test.resolve(taskResult)
},
})

store.dispatch(testAction1('a'))

expect(await fork1Test).toHaveProperty('status', 'cancelled')
middleware.clear()
store.dispatch(testAction1('b'))

expect(await fork1Test).toHaveProperty('status', 'cancelled')
})
})

describe('Listener API', () => {
Expand Down

0 comments on commit e9230e8

Please sign in to comment.