Repository: tannerlinsley/react-query
Pull request #3643 (2022-05-25 16:01:03)

fix: make sure isDataEqual option is respected everywhere where we use structuralSharing

vercel[bot]: **The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Updated |
| :--- | :----- | :------ | :------ |
| **react-query** | 🔄 Building ([Inspect](https://vercel.com/tanstack/react-query/7Td2sZv2bgzBPLABgvGLvBUvPQJ2)) |  | May 25, 2022 at 4:01PM (UTC) |



Patch:
diff --git a/src/core/query.ts b/src/core/query.ts
index ce70b56dc..71d833d13 100644
--- a/src/core/query.ts
+++ b/src/core/query.ts
@@ -1,9 +1,4 @@
-import {
-  getAbortController,
-  noop,
-  replaceEqualDeep,
-  timeUntilStale,
-} from './utils'
+import { getAbortController, noop, replaceData, timeUntilStale } from './utils'
 import type {
   InitialDataFunction,
   QueryKey,
@@ -195,16 +190,11 @@ export class Query<
     }
   }
 
-  setData(data: TData, options?: SetDataOptions & { manual: boolean }): TData {
-    const prevData = this.state.data
-
-    // Use prev data if an isDataEqual function is defined and returns `true`
-    if (this.options.isDataEqual?.(prevData, data)) {
-      data = prevData as TData
-    } else if (this.options.structuralSharing !== false) {
-      // Structurally share data between prev and new data if needed
-      data = replaceEqualDeep(prevData, data)
-    }
+  setData(
+    newData: TData,
+    options?: SetDataOptions & { manual: boolean }
+  ): TData {
+    const data = replaceData(this.state.data, newData, this.options)
 
     // Set data and mark it as cached
     this.dispatch({
diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts
index 68625570b..46e2e6209 100644
--- a/src/core/queryObserver.ts
+++ b/src/core/queryObserver.ts
@@ -3,7 +3,7 @@ import {
   isServer,
   isValidTimeout,
   noop,
-  replaceEqualDeep,
+  replaceData,
   shallowEqualObjects,
   timeUntilStale,
 } from './utils'
@@ -467,9 +467,7 @@ export class QueryObserver<
         try {
           this.selectFn = options.select
           data = options.select(state.data)
-          if (options.structuralSharing !== false) {
-            data = replaceEqualDeep(prevResult?.data, data)
-          }
+          data = replaceData(prevResult?.data, data, options)
           this.selectResult = data
           this.selectError = null
         } catch (selectError) {
@@ -507,12 +505,11 @@ export class QueryObserver<
         if (options.select && typeof placeholderData !== 'undefined') {
           try {
             placeholderData = options.select(placeholderData)
-            if (options.structuralSharing !== false) {
-              placeholderData = replaceEqualDeep(
-                prevResult?.data,
-                placeholderData
-              )
-            }
+            placeholderData = replaceData(
+              prevResult?.data,
+              placeholderData,
+              options
+            )
             this.selectError = null
           } catch (selectError) {
             if (process.env.NODE_ENV !== 'production') {
diff --git a/src/core/utils.ts b/src/core/utils.ts
index ec5c7df77..8fdb107b5 100644
--- a/src/core/utils.ts
+++ b/src/core/utils.ts
@@ -413,3 +413,17 @@ export function getAbortController(): AbortController | undefined {
     return new AbortController()
   }
 }
+
+export function replaceData<
+  TData,
+  TOptions extends QueryOptions<any, any, any, any>
+>(prevData: TData | undefined, data: TData, options: TOptions): TData {
+  // Use prev data if an isDataEqual function is defined and returns `true`
+  if (options.isDataEqual?.(prevData, data)) {
+    return prevData as TData
+  } else if (options.structuralSharing !== false) {
+    // Structurally share data between prev and new data if needed
+    return replaceEqualDeep(prevData, data)
+  }
+  return data
+}


Test patch:
diff --git a/src/core/tests/queryObserver.test.tsx b/src/core/tests/queryObserver.test.tsx
index bafdf46fd..860d24866 100644
--- a/src/core/tests/queryObserver.test.tsx
+++ b/src/core/tests/queryObserver.test.tsx
@@ -630,6 +630,35 @@ describe('queryObserver', () => {
     unsubscribe()
   })
 
+  test('should prefer isDataEqual to structuralSharing', async () => {
+    const key = queryKey()
+
+    const data = { value: 'data' }
+    const newData = { value: 'data' }
+
+    const observer = new QueryObserver(queryClient, {
+      queryKey: key,
+      queryFn: () => data,
+    })
+
+    const unsubscribe = observer.subscribe(() => undefined)
+
+    await sleep(10)
+    expect(observer.getCurrentResult().data).toBe(data)
+
+    observer.setOptions({
+      queryKey: key,
+      queryFn: () => newData,
+      isDataEqual: () => true,
+      structuralSharing: false,
+    })
+
+    await observer.refetch()
+    expect(observer.getCurrentResult().data).toBe(data)
+
+    unsubscribe()
+  })
+
   test('select function error using placeholderdata should log an error', () => {
     const key = queryKey()