From 1de42aaabf3241eda9c9a6961daf731652aab9fb Mon Sep 17 00:00:00 2001 From: Josep M Sobrepere Date: Thu, 2 May 2019 17:40:07 +0200 Subject: [PATCH] Avoid unnecessary selector evaluations --- src/hooks/useSelector.js | 11 +++++------ test/hooks/useSelector.spec.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/hooks/useSelector.js b/src/hooks/useSelector.js index 8e40cfa10..e74f62e65 100644 --- a/src/hooks/useSelector.js +++ b/src/hooks/useSelector.js @@ -52,10 +52,12 @@ export function useSelector(selector) { const latestSubscriptionCallbackError = useRef() const latestSelector = useRef(selector) - let selectedState = undefined + let latestSelectedState = useRef() try { - selectedState = selector(store.getState()) + latestSelectedState.current = useMemo(() => selector(store.getState()), [ + selector + ]) } catch (err) { let errorMessage = `An error occured while selecting the store state: ${ err.message @@ -70,11 +72,8 @@ export function useSelector(selector) { throw new Error(errorMessage) } - const latestSelectedState = useRef(selectedState) - useIsomorphicLayoutEffect(() => { latestSelector.current = selector - latestSelectedState.current = selectedState latestSubscriptionCallbackError.current = undefined }) @@ -107,5 +106,5 @@ export function useSelector(selector) { return () => subscription.tryUnsubscribe() }, [store, subscription]) - return selectedState + return latestSelectedState.current } diff --git a/test/hooks/useSelector.spec.js b/test/hooks/useSelector.spec.js index 74043f1c1..cebea609e 100644 --- a/test/hooks/useSelector.spec.js +++ b/test/hooks/useSelector.spec.js @@ -183,6 +183,34 @@ describe('React', () => { expect(renderedItems).toEqual([0, 0]) }) + + it('uses the latest selector', () => { + let selectorId = 0 + let forceRender + + const Comp = () => { + const [, f] = useReducer(c => c + 1, 0) + forceRender = f + const renderedSelectorId = selectorId++ + const value = useSelector(() => renderedSelectorId) + renderedItems.push(value) + return
+ } + + rtl.render( + + + + ) + + rtl.act(forceRender) + + // this line verifies the susbcription callback uses the same memoized selector and therefore + // does not cause a re-render + store.dispatch({ type: '' }) + + expect(renderedItems).toEqual([0, 1]) + }) }) describe('edge cases', () => {