Repository: tannerlinsley/react-query
Pull request #3135 (2021-12-28 00:36:34)

refactor(fetchTodoById): use tripple equal instead of double equal
Just something I found when playing around in the development enviroment of this project. No biggie - but still :).

vercel[bot]: This pull request is being automatically deployed with Vercel ([learn more](https://vercel.link/github-learn-more)).  
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: [https://vercel.com/tanstack/react-query/EnuxKShc2BX23YQEAKrGZjeJHNvG](https://vercel.com/tanstack/react-query/EnuxKShc2BX23YQEAKrGZjeJHNvG)  
✅ Preview: *In Progress*


codesandbox[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/tannerlinsley/react-query/pr/3135/builds/203543) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 2cbee1ce2d432711355e782eb68a82f8659fe737:

|Sandbox| Source |
|--|--|
|[tannerlinsley/react-query: basic](https://codesandbox.io/s/tannerlinsley-react-query-basic-jxunf)| Configuration |
|[tannerlinsley/react-query](https://codesandbox.io/s/tannerlinsley-react-query-hzru5)| Configuration |


Patch:
diff --git a/examples/playground/src/index.js b/examples/playground/src/index.js
index ee96cf5f9..475e1ca0c 100644
--- a/examples/playground/src/index.js
+++ b/examples/playground/src/index.js
@@ -397,7 +397,7 @@ function fetchTodoById({ id }) {
           new Error(JSON.stringify({ fetchTodoById: { id } }, null, 2))
         );
       }
-      resolve(list.find((d) => d.id == id));
+      resolve(list.find((d) => d.id === id));
     }, queryTimeMin + Math.random() * (queryTimeMax - queryTimeMin));
   });
 }
diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts
index 7dd759727..652fb2a25 100644
--- a/src/core/queryObserver.ts
+++ b/src/core/queryObserver.ts
@@ -69,7 +69,10 @@ export class QueryObserver<
   >
   private previousQueryResult?: QueryObserverResult<TData, TError>
   private previousSelectError: Error | null
-  private previousSelectFn?: (data: TQueryData) => TData
+  private previousSelect?: {
+    fn: (data: TQueryData) => TData
+    result: TData
+  }
   private staleTimeoutId?: number
   private refetchIntervalId?: number
   private currentRefetchInterval?: number | false
@@ -497,14 +500,17 @@ export class QueryObserver<
       if (
         prevResult &&
         state.data === prevResultState?.data &&
-        options.select === this.previousSelectFn &&
+        options.select === this.previousSelect?.fn &&
         !this.previousSelectError
       ) {
-        data = prevResult.data
+        data = this.previousSelect.result
       } else {
         try {
-          this.previousSelectFn = options.select
           data = options.select(state.data)
+          this.previousSelect = {
+            fn: options.select,
+            result: data,
+          }
           if (options.structuralSharing !== false) {
             data = replaceEqualDeep(prevResult?.data, data)
           }


Test patch:
diff --git a/src/react/tests/useQuery.test.tsx b/src/react/tests/useQuery.test.tsx
index 5170c51c5..f93ac90ec 100644
--- a/src/react/tests/useQuery.test.tsx
+++ b/src/react/tests/useQuery.test.tsx
@@ -3998,6 +3998,56 @@ describe('useQuery', () => {
     expect(selectRun).toBe(3)
   })
 
+  it('select should always return the correct state', async () => {
+    const key1 = queryKey()
+
+    function Page() {
+      const [count, inc] = React.useReducer(prev => prev + 1, 2)
+      const [forceValue, forceUpdate] = React.useReducer(prev => prev + 1, 1)
+
+      const state = useQuery(
+        key1,
+        async () => {
+          await sleep(10)
+          return 0
+        },
+        {
+          select: React.useCallback(
+            (data: number) => {
+              return `selected ${data + count}`
+            },
+            [count]
+          ),
+          placeholderData: 99,
+        }
+      )
+
+      return (
+        <div>
+          <h2>Data: {state.data}</h2>
+          <h2>forceValue: {forceValue}</h2>
+          <button onClick={inc}>inc: {count}</button>
+          <button onClick={forceUpdate}>forceUpdate</button>
+        </div>
+      )
+    }
+
+    const rendered = renderWithClient(queryClient, <Page />)
+    await waitFor(() => rendered.getByText('Data: selected 101')) // 99 + 2
+
+    await waitFor(() => rendered.getByText('Data: selected 2')) // 0 + 2
+
+    rendered.getByRole('button', { name: /inc/i }).click()
+
+    await waitFor(() => rendered.getByText('Data: selected 3')) // 0 + 3
+
+    rendered.getByRole('button', { name: /forceUpdate/i }).click()
+
+    await waitFor(() => rendered.getByText('forceValue: 2'))
+    // data should still be 3 after an independent re-render
+    await waitFor(() => rendered.getByText('Data: selected 3'))
+  })
+
   it('should cancel the query function when there are no more subscriptions', async () => {
     const key = queryKey()
     let cancelFn: jest.Mock = jest.fn()