Skip to content

Commit fb2bde7

Browse files
committed
Add initial shallow comparison test
1 parent 82abf97 commit fb2bde7

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

test/hooks/useSelector.spec.tsx

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from 'react-redux'
3131
import type { Action, AnyAction, Store } from 'redux'
3232
import { createStore } from 'redux'
33+
import { configureStore, createSlice } from '@reduxjs/toolkit'
3334

3435
// disable checks by default
3536
function ProviderMock<A extends Action<any> = AnyAction, S = unknown>({
@@ -441,6 +442,137 @@ describe('React', () => {
441442
expect(selector).toHaveBeenCalledTimes(2)
442443
expect(renderedItems.length).toEqual(2)
443444
})
445+
446+
it.only('only calls selectors if the state they depend on has changed', () => {
447+
const sliceA = createSlice({
448+
name: 'a',
449+
initialState: 0,
450+
reducers: {
451+
incrementA: (state) => state + 1,
452+
},
453+
})
454+
455+
const sliceB = createSlice({
456+
name: 'b',
457+
initialState: 0,
458+
reducers: {
459+
incrementB: (state) => state + 1,
460+
},
461+
462+
extraReducers: (builder) => {
463+
builder.addCase('incrementBC', (state) => state + 1)
464+
},
465+
})
466+
467+
const sliceC = createSlice({
468+
name: 'c',
469+
initialState: 0,
470+
reducers: {
471+
incrementC: (state) => state + 1,
472+
},
473+
extraReducers: (builder) => {
474+
builder.addCase('incrementBC', (state) => state + 1)
475+
},
476+
})
477+
478+
const store = configureStore({
479+
reducer: {
480+
a: sliceA.reducer,
481+
b: sliceB.reducer,
482+
c: sliceC.reducer,
483+
},
484+
})
485+
486+
type RootState = ReturnType<typeof store.getState>
487+
488+
type StateKeys = 'a' | 'b' | 'c'
489+
490+
const { incrementA } = sliceA.actions
491+
const { incrementB } = sliceB.actions
492+
const { incrementC } = sliceC.actions
493+
494+
let selectorACalls = 0
495+
let selectorBCalls = 0
496+
let selectorCCalls = 0
497+
let selectorABCalls = 0
498+
499+
const selectA = (state: RootState) => (selectorACalls++, state.a)
500+
const selectB = (state: RootState) => (selectorBCalls++, state.b)
501+
const selectC = (state: RootState) => (selectorCCalls++, state.c)
502+
const selectAB = (state: RootState) => {
503+
selectorABCalls++
504+
return state.a + state.b
505+
}
506+
507+
function SliceA() {
508+
const a = useSelector(selectA)
509+
return null
510+
}
511+
512+
function SliceB() {
513+
const b = useSelector(selectB)
514+
return null
515+
}
516+
517+
function SliceC() {
518+
const c = useSelector(selectC)
519+
return null
520+
}
521+
522+
function AB() {
523+
const ab = useSelector(selectAB)
524+
return null
525+
}
526+
527+
rtl.render(
528+
<ProviderMock store={store}>
529+
<SliceA />
530+
<SliceB />
531+
<SliceC />
532+
<AB />
533+
</ProviderMock>,
534+
)
535+
expect(selectorACalls).toBe(1)
536+
expect(selectorBCalls).toBe(1)
537+
expect(selectorCCalls).toBe(1)
538+
expect(selectorABCalls).toBe(1)
539+
540+
rtl.act(() => {
541+
store.dispatch(incrementA())
542+
})
543+
544+
expect(selectorACalls).toBe(2)
545+
expect(selectorBCalls).toBe(1)
546+
expect(selectorCCalls).toBe(1)
547+
expect(selectorABCalls).toBe(2)
548+
549+
rtl.act(() => {
550+
store.dispatch(incrementB())
551+
})
552+
553+
expect(selectorACalls).toBe(2)
554+
expect(selectorBCalls).toBe(2)
555+
expect(selectorCCalls).toBe(1)
556+
expect(selectorABCalls).toBe(3)
557+
558+
rtl.act(() => {
559+
store.dispatch(incrementC())
560+
})
561+
562+
expect(selectorACalls).toBe(2)
563+
expect(selectorBCalls).toBe(2)
564+
expect(selectorCCalls).toBe(2)
565+
expect(selectorABCalls).toBe(3)
566+
567+
rtl.act(() => {
568+
store.dispatch({ type: 'incrementBC' })
569+
})
570+
571+
expect(selectorACalls).toBe(2)
572+
expect(selectorBCalls).toBe(3)
573+
expect(selectorCCalls).toBe(3)
574+
expect(selectorABCalls).toBe(4)
575+
})
444576
})
445577

446578
it('uses the latest selector', () => {

0 commit comments

Comments
 (0)