Skip to content

Commit

Permalink
optimize subscriber notifications, add test
Browse files Browse the repository at this point in the history
  • Loading branch information
gentlee committed Dec 1, 2023
1 parent e124f0b commit 32becea
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 37 deletions.
10 changes: 7 additions & 3 deletions src/createStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ export function createStore<
throw new Error('Reducers may not dispatch actions.')
}

const previousState = currentState
try {
isDispatching = true
currentState = currentReducer(currentState, action)
Expand All @@ -302,9 +303,12 @@ export function createStore<
}

const listeners = (currentListeners = nextListeners)
listeners.forEach(listener => {
listener()
})
if (previousState !== currentState) {
listeners.forEach(listener => {
listener()
})
}

return action
}

Expand Down
62 changes: 28 additions & 34 deletions test/createStore.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,55 +207,60 @@ describe('createStore', () => {
])
})

it(`doesn't notify listeners when state didn't change after action`, () => {
const store = createStore(reducers.todos)
const listener = vi.fn()

store.subscribe(listener)
// @ts-expect-error
store.dispatch(unknownAction())

expect(listener.mock.calls.length).toBe(0)
})

it('supports multiple subscriptions', () => {
const store = createStore(reducers.todos)
const listenerA = vi.fn()
const listenerB = vi.fn()

let unsubscribeA = store.subscribe(listenerA)
// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listenerA.mock.calls.length).toBe(1)
expect(listenerB.mock.calls.length).toBe(0)

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listenerA.mock.calls.length).toBe(2)
expect(listenerB.mock.calls.length).toBe(0)

const unsubscribeB = store.subscribe(listenerB)
expect(listenerA.mock.calls.length).toBe(2)
expect(listenerB.mock.calls.length).toBe(0)

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listenerA.mock.calls.length).toBe(3)
expect(listenerB.mock.calls.length).toBe(1)

unsubscribeA()
expect(listenerA.mock.calls.length).toBe(3)
expect(listenerB.mock.calls.length).toBe(1)

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listenerA.mock.calls.length).toBe(3)
expect(listenerB.mock.calls.length).toBe(2)

unsubscribeB()
expect(listenerA.mock.calls.length).toBe(3)
expect(listenerB.mock.calls.length).toBe(2)

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listenerA.mock.calls.length).toBe(3)
expect(listenerB.mock.calls.length).toBe(2)

unsubscribeA = store.subscribe(listenerA)
expect(listenerA.mock.calls.length).toBe(3)
expect(listenerB.mock.calls.length).toBe(2)

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listenerA.mock.calls.length).toBe(4)
expect(listenerB.mock.calls.length).toBe(2)
})
Expand All @@ -271,8 +276,7 @@ describe('createStore', () => {
unsubscribeA()
unsubscribeA()

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listenerA.mock.calls.length).toBe(0)
expect(listenerB.mock.calls.length).toBe(1)
})
Expand All @@ -287,8 +291,7 @@ describe('createStore', () => {
unsubscribeSecond()
unsubscribeSecond()

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listener.mock.calls.length).toBe(1)
})

Expand All @@ -305,10 +308,8 @@ describe('createStore', () => {
})
store.subscribe(listenerC)

// @ts-expect-error
store.dispatch(unknownAction())
// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
store.dispatch(addTodo(''))

expect(listenerA.mock.calls.length).toBe(2)
expect(listenerB.mock.calls.length).toBe(1)
Expand All @@ -335,14 +336,12 @@ describe('createStore', () => {
)
unsubscribeHandles.push(store.subscribe(() => listener3()))

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listener1.mock.calls.length).toBe(1)
expect(listener2.mock.calls.length).toBe(1)
expect(listener3.mock.calls.length).toBe(1)

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listener1.mock.calls.length).toBe(1)
expect(listener2.mock.calls.length).toBe(1)
expect(listener3.mock.calls.length).toBe(1)
Expand All @@ -369,14 +368,12 @@ describe('createStore', () => {
maybeAddThirdListener()
})

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listener1.mock.calls.length).toBe(1)
expect(listener2.mock.calls.length).toBe(1)
expect(listener3.mock.calls.length).toBe(0)

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listener1.mock.calls.length).toBe(2)
expect(listener2.mock.calls.length).toBe(2)
expect(listener3.mock.calls.length).toBe(1)
Expand All @@ -400,8 +397,7 @@ describe('createStore', () => {

unsubscribe1()
unsubscribe4 = store.subscribe(listener4)
// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))

expect(listener1.mock.calls.length).toBe(1)
expect(listener2.mock.calls.length).toBe(1)
Expand All @@ -411,16 +407,14 @@ describe('createStore', () => {
store.subscribe(listener2)
store.subscribe(listener3)

// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listener1.mock.calls.length).toBe(1)
expect(listener2.mock.calls.length).toBe(2)
expect(listener3.mock.calls.length).toBe(2)
expect(listener4.mock.calls.length).toBe(1)

unsubscribe4()
// @ts-expect-error
store.dispatch(unknownAction())
store.dispatch(addTodo(''))
expect(listener1.mock.calls.length).toBe(1)
expect(listener2.mock.calls.length).toBe(3)
expect(listener3.mock.calls.length).toBe(3)
Expand Down

0 comments on commit 32becea

Please sign in to comment.