diff --git a/src/hooks/useSelector.js b/src/hooks/useSelector.js index da23c6605..0324a262e 100644 --- a/src/hooks/useSelector.js +++ b/src/hooks/useSelector.js @@ -33,7 +33,16 @@ function useSelectorWithStoreAndSubscription( storeState !== latestStoreState.current || latestSubscriptionCallbackError.current ) { - selectedState = selector(storeState) + const newSelectedState = selector(storeState) + // ensure latest selected state is reused so that a curtom equality function can result in identical references + if ( + latestSelectedState.current === undefined || + !equalityFn(newSelectedState, latestSelectedState.current) + ) { + selectedState = newSelectedState + } else { + selectedState = latestSelectedState.current + } } else { selectedState = latestSelectedState.current } diff --git a/test/hooks/useSelector.spec.js b/test/hooks/useSelector.spec.js index 38f4bc391..3a0fcfd7d 100644 --- a/test/hooks/useSelector.spec.js +++ b/test/hooks/useSelector.spec.js @@ -412,6 +412,35 @@ describe('React', () => { spy.mockRestore() }) + + it('reuse latest selected state on selector re-run', () => { + store = createStore(({ count } = { count: -1 }) => ({ + count: count + 1, + })) + + const alwaysEqual = () => true + + const Comp = () => { + // triggers render on store change + useSelector((s) => s.count) + const array = useSelector(() => [1, 2, 3], alwaysEqual) + renderedItems.push(array) + return
+ } + + rtl.render( + + + + ) + + expect(renderedItems.length).toBe(1) + + store.dispatch({ type: '' }) + + expect(renderedItems.length).toBe(2) + expect(renderedItems[0]).toBe(renderedItems[1]) + }) }) describe('error handling for invalid arguments', () => {