Skip to content

Commit 270efe6

Browse files
authored
fix: make sure isDataEqual option is respected everywhere where we use structuralSharing (#3643)
1 parent 40b921a commit 270efe6

File tree

4 files changed

+56
-26
lines changed

4 files changed

+56
-26
lines changed

src/core/query.ts

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import {
2-
getAbortController,
3-
noop,
4-
replaceEqualDeep,
5-
timeUntilStale,
6-
} from './utils'
1+
import { getAbortController, noop, replaceData, timeUntilStale } from './utils'
72
import type {
83
InitialDataFunction,
94
QueryKey,
@@ -195,16 +190,11 @@ export class Query<
195190
}
196191
}
197192

198-
setData(data: TData, options?: SetDataOptions & { manual: boolean }): TData {
199-
const prevData = this.state.data
200-
201-
// Use prev data if an isDataEqual function is defined and returns `true`
202-
if (this.options.isDataEqual?.(prevData, data)) {
203-
data = prevData as TData
204-
} else if (this.options.structuralSharing !== false) {
205-
// Structurally share data between prev and new data if needed
206-
data = replaceEqualDeep(prevData, data)
207-
}
193+
setData(
194+
newData: TData,
195+
options?: SetDataOptions & { manual: boolean }
196+
): TData {
197+
const data = replaceData(this.state.data, newData, this.options)
208198

209199
// Set data and mark it as cached
210200
this.dispatch({

src/core/queryObserver.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
isServer,
44
isValidTimeout,
55
noop,
6-
replaceEqualDeep,
6+
replaceData,
77
shallowEqualObjects,
88
timeUntilStale,
99
} from './utils'
@@ -467,9 +467,7 @@ export class QueryObserver<
467467
try {
468468
this.selectFn = options.select
469469
data = options.select(state.data)
470-
if (options.structuralSharing !== false) {
471-
data = replaceEqualDeep(prevResult?.data, data)
472-
}
470+
data = replaceData(prevResult?.data, data, options)
473471
this.selectResult = data
474472
this.selectError = null
475473
} catch (selectError) {
@@ -507,12 +505,11 @@ export class QueryObserver<
507505
if (options.select && typeof placeholderData !== 'undefined') {
508506
try {
509507
placeholderData = options.select(placeholderData)
510-
if (options.structuralSharing !== false) {
511-
placeholderData = replaceEqualDeep(
512-
prevResult?.data,
513-
placeholderData
514-
)
515-
}
508+
placeholderData = replaceData(
509+
prevResult?.data,
510+
placeholderData,
511+
options
512+
)
516513
this.selectError = null
517514
} catch (selectError) {
518515
if (process.env.NODE_ENV !== 'production') {

src/core/tests/queryObserver.test.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,35 @@ describe('queryObserver', () => {
630630
unsubscribe()
631631
})
632632

633+
test('should prefer isDataEqual to structuralSharing', async () => {
634+
const key = queryKey()
635+
636+
const data = { value: 'data' }
637+
const newData = { value: 'data' }
638+
639+
const observer = new QueryObserver(queryClient, {
640+
queryKey: key,
641+
queryFn: () => data,
642+
})
643+
644+
const unsubscribe = observer.subscribe(() => undefined)
645+
646+
await sleep(10)
647+
expect(observer.getCurrentResult().data).toBe(data)
648+
649+
observer.setOptions({
650+
queryKey: key,
651+
queryFn: () => newData,
652+
isDataEqual: () => true,
653+
structuralSharing: false,
654+
})
655+
656+
await observer.refetch()
657+
expect(observer.getCurrentResult().data).toBe(data)
658+
659+
unsubscribe()
660+
})
661+
633662
test('select function error using placeholderdata should log an error', () => {
634663
const key = queryKey()
635664

src/core/utils.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,3 +413,17 @@ export function getAbortController(): AbortController | undefined {
413413
return new AbortController()
414414
}
415415
}
416+
417+
export function replaceData<
418+
TData,
419+
TOptions extends QueryOptions<any, any, any, any>
420+
>(prevData: TData | undefined, data: TData, options: TOptions): TData {
421+
// Use prev data if an isDataEqual function is defined and returns `true`
422+
if (options.isDataEqual?.(prevData, data)) {
423+
return prevData as TData
424+
} else if (options.structuralSharing !== false) {
425+
// Structurally share data between prev and new data if needed
426+
return replaceEqualDeep(prevData, data)
427+
}
428+
return data
429+
}

0 commit comments

Comments
 (0)