Skip to content

Commit 062d5fc

Browse files
committed
useSelector: Allow optional compararison function
Export shallowEqual function
1 parent ea95382 commit 062d5fc

File tree

4 files changed

+42
-7
lines changed

4 files changed

+42
-7
lines changed

package-lock.json

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/hooks/useSelector.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import Subscription from '../utils/Subscription'
1414
const useIsomorphicLayoutEffect =
1515
typeof window !== 'undefined' ? useLayoutEffect : useEffect
1616

17+
const refEquality = (a, b) => a === b
18+
1719
/**
1820
* A hook to access the redux store's state. This hook takes a selector function
1921
* as an argument. The selector is called with the store state.
@@ -23,6 +25,7 @@ const useIsomorphicLayoutEffect =
2325
* useful if you provide a selector that memoizes values).
2426
*
2527
* @param {Function} selector the selector function
28+
* @param {Function} equalityFn the function that will be used to determine equality
2629
*
2730
* @returns {any} the selected state
2831
*
@@ -37,7 +40,7 @@ const useIsomorphicLayoutEffect =
3740
* return <div>{counter}</div>
3841
* }
3942
*/
40-
export function useSelector(selector) {
43+
export function useSelector(selector, equalityFn = refEquality) {
4144
invariant(selector, `You must pass a selector to useSelectors`)
4245

4346
const { store, subscription: contextSub } = useReduxContext()
@@ -82,7 +85,7 @@ export function useSelector(selector) {
8285
try {
8386
const newSelectedState = latestSelector.current(store.getState())
8487

85-
if (newSelectedState === latestSelectedState.current) {
88+
if (equalityFn(newSelectedState, latestSelectedState.current)) {
8689
return
8790
}
8891

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { useStore } from './hooks/useStore'
99

1010
import { setBatch } from './utils/batch'
1111
import { unstable_batchedUpdates as batch } from './utils/reactBatchedUpdates'
12+
import shallowEqual from './utils/shallowEqual'
1213

1314
setBatch(batch)
1415

@@ -20,5 +21,6 @@ export {
2021
batch,
2122
useDispatch,
2223
useSelector,
23-
useStore
24+
useStore,
25+
shallowEqual
2426
}

test/hooks/useSelector.spec.js

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import React from 'react'
44
import { createStore } from 'redux'
55
import { renderHook, act } from 'react-hooks-testing-library'
66
import * as rtl from 'react-testing-library'
7-
import { Provider as ProviderMock, useSelector } from '../../src/index.js'
7+
import {
8+
Provider as ProviderMock,
9+
useSelector,
10+
shallowEqual
11+
} from '../../src/index.js'
812
import { useReduxContext } from '../../src/hooks/useReduxContext'
913

1014
describe('React', () => {
@@ -128,7 +132,7 @@ describe('React', () => {
128132
})
129133

130134
describe('performance optimizations and bail-outs', () => {
131-
it('should compare the selected state by reference to prevent unnecessary updates', () => {
135+
it('defaults to ref-equality to prevent unnecessary updates', () => {
132136
const state = {}
133137
store = createStore(() => state)
134138

@@ -150,6 +154,33 @@ describe('React', () => {
150154

151155
expect(renderedItems.length).toBe(1)
152156
})
157+
158+
it('allows other equality functions to prevent unnecessary updates', () => {
159+
store = createStore(
160+
({ count, stable } = { count: -1, stable: {} }) => ({
161+
count: count + 1,
162+
stable
163+
})
164+
)
165+
166+
const Comp = () => {
167+
const value = useSelector(s => Object.keys(s), shallowEqual)
168+
renderedItems.push(value)
169+
return <div />
170+
}
171+
172+
rtl.render(
173+
<ProviderMock store={store}>
174+
<Comp />
175+
</ProviderMock>
176+
)
177+
178+
expect(renderedItems.length).toBe(1)
179+
180+
store.dispatch({ type: '' })
181+
182+
expect(renderedItems.length).toBe(1)
183+
})
153184
})
154185

155186
describe('edge cases', () => {

0 commit comments

Comments
 (0)