Skip to content

Commit 32becea

Browse files
committed
optimize subscriber notifications, add test
1 parent e124f0b commit 32becea

File tree

2 files changed

+35
-37
lines changed

2 files changed

+35
-37
lines changed

src/createStore.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ export function createStore<
294294
throw new Error('Reducers may not dispatch actions.')
295295
}
296296

297+
const previousState = currentState
297298
try {
298299
isDispatching = true
299300
currentState = currentReducer(currentState, action)
@@ -302,9 +303,12 @@ export function createStore<
302303
}
303304

304305
const listeners = (currentListeners = nextListeners)
305-
listeners.forEach(listener => {
306-
listener()
307-
})
306+
if (previousState !== currentState) {
307+
listeners.forEach(listener => {
308+
listener()
309+
})
310+
}
311+
308312
return action
309313
}
310314

test/createStore.spec.ts

+28-34
Original file line numberDiff line numberDiff line change
@@ -207,55 +207,60 @@ describe('createStore', () => {
207207
])
208208
})
209209

210+
it(`doesn't notify listeners when state didn't change after action`, () => {
211+
const store = createStore(reducers.todos)
212+
const listener = vi.fn()
213+
214+
store.subscribe(listener)
215+
// @ts-expect-error
216+
store.dispatch(unknownAction())
217+
218+
expect(listener.mock.calls.length).toBe(0)
219+
})
220+
210221
it('supports multiple subscriptions', () => {
211222
const store = createStore(reducers.todos)
212223
const listenerA = vi.fn()
213224
const listenerB = vi.fn()
214225

215226
let unsubscribeA = store.subscribe(listenerA)
216-
// @ts-expect-error
217-
store.dispatch(unknownAction())
227+
store.dispatch(addTodo(''))
218228
expect(listenerA.mock.calls.length).toBe(1)
219229
expect(listenerB.mock.calls.length).toBe(0)
220230

221-
// @ts-expect-error
222-
store.dispatch(unknownAction())
231+
store.dispatch(addTodo(''))
223232
expect(listenerA.mock.calls.length).toBe(2)
224233
expect(listenerB.mock.calls.length).toBe(0)
225234

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

230-
// @ts-expect-error
231-
store.dispatch(unknownAction())
239+
store.dispatch(addTodo(''))
232240
expect(listenerA.mock.calls.length).toBe(3)
233241
expect(listenerB.mock.calls.length).toBe(1)
234242

235243
unsubscribeA()
236244
expect(listenerA.mock.calls.length).toBe(3)
237245
expect(listenerB.mock.calls.length).toBe(1)
238246

239-
// @ts-expect-error
240-
store.dispatch(unknownAction())
247+
store.dispatch(addTodo(''))
241248
expect(listenerA.mock.calls.length).toBe(3)
242249
expect(listenerB.mock.calls.length).toBe(2)
243250

244251
unsubscribeB()
245252
expect(listenerA.mock.calls.length).toBe(3)
246253
expect(listenerB.mock.calls.length).toBe(2)
247254

248-
// @ts-expect-error
249-
store.dispatch(unknownAction())
255+
store.dispatch(addTodo(''))
250256
expect(listenerA.mock.calls.length).toBe(3)
251257
expect(listenerB.mock.calls.length).toBe(2)
252258

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

257-
// @ts-expect-error
258-
store.dispatch(unknownAction())
263+
store.dispatch(addTodo(''))
259264
expect(listenerA.mock.calls.length).toBe(4)
260265
expect(listenerB.mock.calls.length).toBe(2)
261266
})
@@ -271,8 +276,7 @@ describe('createStore', () => {
271276
unsubscribeA()
272277
unsubscribeA()
273278

274-
// @ts-expect-error
275-
store.dispatch(unknownAction())
279+
store.dispatch(addTodo(''))
276280
expect(listenerA.mock.calls.length).toBe(0)
277281
expect(listenerB.mock.calls.length).toBe(1)
278282
})
@@ -287,8 +291,7 @@ describe('createStore', () => {
287291
unsubscribeSecond()
288292
unsubscribeSecond()
289293

290-
// @ts-expect-error
291-
store.dispatch(unknownAction())
294+
store.dispatch(addTodo(''))
292295
expect(listener.mock.calls.length).toBe(1)
293296
})
294297

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

308-
// @ts-expect-error
309-
store.dispatch(unknownAction())
310-
// @ts-expect-error
311-
store.dispatch(unknownAction())
311+
store.dispatch(addTodo(''))
312+
store.dispatch(addTodo(''))
312313

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

338-
// @ts-expect-error
339-
store.dispatch(unknownAction())
339+
store.dispatch(addTodo(''))
340340
expect(listener1.mock.calls.length).toBe(1)
341341
expect(listener2.mock.calls.length).toBe(1)
342342
expect(listener3.mock.calls.length).toBe(1)
343343

344-
// @ts-expect-error
345-
store.dispatch(unknownAction())
344+
store.dispatch(addTodo(''))
346345
expect(listener1.mock.calls.length).toBe(1)
347346
expect(listener2.mock.calls.length).toBe(1)
348347
expect(listener3.mock.calls.length).toBe(1)
@@ -369,14 +368,12 @@ describe('createStore', () => {
369368
maybeAddThirdListener()
370369
})
371370

372-
// @ts-expect-error
373-
store.dispatch(unknownAction())
371+
store.dispatch(addTodo(''))
374372
expect(listener1.mock.calls.length).toBe(1)
375373
expect(listener2.mock.calls.length).toBe(1)
376374
expect(listener3.mock.calls.length).toBe(0)
377375

378-
// @ts-expect-error
379-
store.dispatch(unknownAction())
376+
store.dispatch(addTodo(''))
380377
expect(listener1.mock.calls.length).toBe(2)
381378
expect(listener2.mock.calls.length).toBe(2)
382379
expect(listener3.mock.calls.length).toBe(1)
@@ -400,8 +397,7 @@ describe('createStore', () => {
400397

401398
unsubscribe1()
402399
unsubscribe4 = store.subscribe(listener4)
403-
// @ts-expect-error
404-
store.dispatch(unknownAction())
400+
store.dispatch(addTodo(''))
405401

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

414-
// @ts-expect-error
415-
store.dispatch(unknownAction())
410+
store.dispatch(addTodo(''))
416411
expect(listener1.mock.calls.length).toBe(1)
417412
expect(listener2.mock.calls.length).toBe(2)
418413
expect(listener3.mock.calls.length).toBe(2)
419414
expect(listener4.mock.calls.length).toBe(1)
420415

421416
unsubscribe4()
422-
// @ts-expect-error
423-
store.dispatch(unknownAction())
417+
store.dispatch(addTodo(''))
424418
expect(listener1.mock.calls.length).toBe(1)
425419
expect(listener2.mock.calls.length).toBe(3)
426420
expect(listener3.mock.calls.length).toBe(3)

0 commit comments

Comments
 (0)