diff --git a/package-lock.json b/package-lock.json
index 83be93f56..0cbd3105d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5010,8 +5010,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "dev": true,
- "optional": true
+ "dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
diff --git a/src/hooks/useSelector.js b/src/hooks/useSelector.js
index 8e40cfa10..8f331f9cd 100644
--- a/src/hooks/useSelector.js
+++ b/src/hooks/useSelector.js
@@ -1,7 +1,6 @@
import { useReducer, useRef, useEffect, useMemo, useLayoutEffect } from 'react'
import invariant from 'invariant'
import { useReduxContext } from './useReduxContext'
-import shallowEqual from '../utils/shallowEqual'
import Subscription from '../utils/Subscription'
// React currently throws a warning when using useLayoutEffect on the server.
@@ -15,6 +14,8 @@ import Subscription from '../utils/Subscription'
const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect
+const refEquality = (a, b) => a === b
+
/**
* A hook to access the redux store's state. This hook takes a selector function
* as an argument. The selector is called with the store state.
@@ -24,6 +25,7 @@ const useIsomorphicLayoutEffect =
* useful if you provide a selector that memoizes values).
*
* @param {Function} selector the selector function
+ * @param {Function} equalityFn the function that will be used to determine equality
*
* @returns {any} the selected state
*
@@ -38,7 +40,7 @@ const useIsomorphicLayoutEffect =
* return
{counter}
* }
*/
-export function useSelector(selector) {
+export function useSelector(selector, equalityFn = refEquality) {
invariant(selector, `You must pass a selector to useSelectors`)
const { store, subscription: contextSub } = useReduxContext()
@@ -83,7 +85,7 @@ export function useSelector(selector) {
try {
const newSelectedState = latestSelector.current(store.getState())
- if (shallowEqual(newSelectedState, latestSelectedState.current)) {
+ if (equalityFn(newSelectedState, latestSelectedState.current)) {
return
}
diff --git a/src/index.js b/src/index.js
index 654d5e7fd..8817a27aa 100644
--- a/src/index.js
+++ b/src/index.js
@@ -9,6 +9,7 @@ import { useStore } from './hooks/useStore'
import { setBatch } from './utils/batch'
import { unstable_batchedUpdates as batch } from './utils/reactBatchedUpdates'
+import shallowEqual from './utils/shallowEqual'
setBatch(batch)
@@ -20,5 +21,6 @@ export {
batch,
useDispatch,
useSelector,
- useStore
+ useStore,
+ shallowEqual
}
diff --git a/test/hooks/useSelector.spec.js b/test/hooks/useSelector.spec.js
index c37c5b6e6..8f9409234 100644
--- a/test/hooks/useSelector.spec.js
+++ b/test/hooks/useSelector.spec.js
@@ -4,7 +4,11 @@ import React from 'react'
import { createStore } from 'redux'
import { renderHook, act } from 'react-hooks-testing-library'
import * as rtl from 'react-testing-library'
-import { Provider as ProviderMock, useSelector } from '../../src/index.js'
+import {
+ Provider as ProviderMock,
+ useSelector,
+ shallowEqual
+} from '../../src/index.js'
import { useReduxContext } from '../../src/hooks/useReduxContext'
describe('React', () => {
@@ -128,7 +132,30 @@ describe('React', () => {
})
describe('performance optimizations and bail-outs', () => {
- it('should shallowly compare the selected state to prevent unnecessary updates', () => {
+ it('defaults to ref-equality to prevent unnecessary updates', () => {
+ const state = {}
+ store = createStore(() => state)
+
+ const Comp = () => {
+ const value = useSelector(s => s)
+ renderedItems.push(value)
+ return
+ }
+
+ rtl.render(
+
+
+
+ )
+
+ expect(renderedItems.length).toBe(1)
+
+ store.dispatch({ type: '' })
+
+ expect(renderedItems.length).toBe(1)
+ })
+
+ it('allows other equality functions to prevent unnecessary updates', () => {
store = createStore(
({ count, stable } = { count: -1, stable: {} }) => ({
count: count + 1,
@@ -137,7 +164,7 @@ describe('React', () => {
)
const Comp = () => {
- const value = useSelector(s => Object.keys(s))
+ const value = useSelector(s => Object.keys(s), shallowEqual)
renderedItems.push(value)
return
}