Repository: reduxjs/react-redux (ts)

Issue:
selector in useSelector called one more time after forceUpdate inside hook
**What is the current behavior?**

When state changed, selector recomputes, and value sets into latestSelectedState. Component will re-render, and selector recomputes again, but its value already in latestSelectedState.current

(selector is arrow function - `const data = useSelector((state) => state.data);`)

**What is the expected behavior?**

Selector recomputes once

I can open PR to fix this, but I don't know, may be selector specially calls twice for some reasons

Pull request description:
Don't re-run the selector after update
Fixes #1697

We can avoid running the selector twice by setting the latestStoreState at the same time as setting the latestSelectedState. If the store state hasn't changed, then the selector won't as well (since it's pure).

Patch:
diff --git a/src/hooks/useSelector.js b/src/hooks/useSelector.js
index 39066e7..87b61f2 100644
--- a/src/hooks/useSelector.js
+++ b/src/hooks/useSelector.js
@@ -64,13 +64,15 @@ function useSelectorWithStoreAndSubscription(
   useIsomorphicLayoutEffect(() => {
     function checkForUpdates() {
       try {
-        const newSelectedState = latestSelector.current(store.getState())
+        const newStoreState = store.getState()
+        const newSelectedState = latestSelector.current(newStoreState)
 
         if (equalityFn(newSelectedState, latestSelectedState.current)) {
           return
         }
 
         latestSelectedState.current = newSelectedState
+        latestStoreState.current = newStoreState
       } catch (err) {
         // we ignore all errors here, since when the component
         // is re-rendered, the selectors are called again, and


Test patch:
diff --git a/test/hooks/useSelector.spec.js b/test/hooks/useSelector.spec.js
index 3a0fcfd..0af0176 100644
--- a/test/hooks/useSelector.spec.js
+++ b/test/hooks/useSelector.spec.js
@@ -38,17 +38,21 @@ describe('React', () => {
         })
 
         it('selects the state and renders the component when the store updates', () => {
-          const { result } = renderHook(() => useSelector((s) => s.count), {
+          const selector = jest.fn((s) => s.count)
+
+          const { result } = renderHook(() => useSelector(selector), {
             wrapper: (props) => <ProviderMock {...props} store={store} />,
           })
 
           expect(result.current).toEqual(0)
+          expect(selector).toHaveBeenCalledTimes(2)
 
           act(() => {
             store.dispatch({ type: '' })
           })
 
           expect(result.current).toEqual(1)
+          expect(selector).toHaveBeenCalledTimes(3)
         })
       })