Skip to content

Commit bd6c2b5

Browse files
authored
feat: run selector if reference changes to enable use of dependencies (#1751)
1 parent 7c08f33 commit bd6c2b5

File tree

2 files changed

+96
-13
lines changed

2 files changed

+96
-13
lines changed

src/core/queryObserver.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ export class QueryObserver<
4949
private currentQuery!: Query<TQueryFnData, TError, TQueryData>
5050
private currentResult!: QueryObserverResult<TData, TError>
5151
private currentResultState?: QueryState<TQueryData, TError>
52+
private previousOptions?: QueryObserverOptions<
53+
TQueryFnData,
54+
TError,
55+
TData,
56+
TQueryData
57+
>
5258
private previousQueryResult?: QueryObserverResult<TData, TError>
5359
private initialDataUpdateCount: number
5460
private initialErrorUpdateCount: number
@@ -155,8 +161,7 @@ export class QueryObserver<
155161
setOptions(
156162
options?: QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>
157163
): void {
158-
const prevOptions = this.options
159-
164+
this.previousOptions = this.options
160165
this.options = this.client.defaultQueryObserverOptions(options)
161166

162167
if (
@@ -168,39 +173,49 @@ export class QueryObserver<
168173

169174
// Keep previous query key if the user does not supply one
170175
if (!this.options.queryKey) {
171-
this.options.queryKey = prevOptions.queryKey
176+
this.options.queryKey = this.previousOptions.queryKey
172177
}
173178

174179
const didUpdateQuery = this.updateQuery()
175180

176181
let optionalFetch
182+
let updateResult
177183
let updateStaleTimeout
178184
let updateRefetchInterval
179185

180-
// If we subscribed to a new query, optionally fetch and update intervals
186+
// If we subscribed to a new query, optionally fetch and update result and timers
181187
if (didUpdateQuery) {
182188
optionalFetch = true
189+
updateResult = true
183190
updateStaleTimeout = true
184191
updateRefetchInterval = true
185192
}
186193

187194
// Optionally fetch if the query became enabled
188-
if (this.options.enabled !== false && prevOptions.enabled === false) {
195+
if (
196+
this.options.enabled !== false &&
197+
this.previousOptions.enabled === false
198+
) {
189199
optionalFetch = true
190200
}
191201

202+
// Update result if the select function changed
203+
if (this.options.select !== this.previousOptions.select) {
204+
updateResult = true
205+
}
206+
192207
// Update stale interval if needed
193208
if (
194-
this.options.enabled !== prevOptions.enabled ||
195-
this.options.staleTime !== prevOptions.staleTime
209+
this.options.enabled !== this.previousOptions.enabled ||
210+
this.options.staleTime !== this.previousOptions.staleTime
196211
) {
197212
updateStaleTimeout = true
198213
}
199214

200215
// Update refetch interval if needed
201216
if (
202-
this.options.enabled !== prevOptions.enabled ||
203-
this.options.refetchInterval !== prevOptions.refetchInterval
217+
this.options.enabled !== this.previousOptions.enabled ||
218+
this.options.refetchInterval !== this.previousOptions.refetchInterval
204219
) {
205220
updateRefetchInterval = true
206221
}
@@ -212,8 +227,7 @@ export class QueryObserver<
212227
}
213228
}
214229

215-
// Update result when subscribing to a new query
216-
if (didUpdateQuery) {
230+
if (updateResult) {
217231
this.updateResult()
218232
}
219233

@@ -399,8 +413,12 @@ export class QueryObserver<
399413
}
400414
// Select data if needed
401415
else if (this.options.select && typeof state.data !== 'undefined') {
402-
// Use the previous select result if the query data did not change
403-
if (this.currentResult && state.data === this.currentResultState?.data) {
416+
// Use the previous select result if the query data and select function did not change
417+
if (
418+
this.currentResult &&
419+
state.data === this.currentResultState?.data &&
420+
this.options.select === this.previousOptions?.select
421+
) {
404422
data = this.currentResult.data
405423
} else {
406424
data = this.options.select(state.data)

src/core/tests/queryObserver.test.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,71 @@ describe('queryObserver', () => {
130130
expect(observerResult2.data).toMatchObject({ myCount: 1 })
131131
})
132132

133+
test('should run the selector again if the selector changed', async () => {
134+
const key = queryKey()
135+
let count = 0
136+
const results: QueryObserverResult[] = []
137+
const queryFn = () => ({ count: 1 })
138+
const select1 = (data: ReturnType<typeof queryFn>) => {
139+
count++
140+
return { myCount: data.count }
141+
}
142+
const select2 = (_data: ReturnType<typeof queryFn>) => {
143+
count++
144+
return { myCount: 99 }
145+
}
146+
const observer = new QueryObserver(queryClient, {
147+
queryKey: key,
148+
queryFn,
149+
select: select1,
150+
})
151+
const unsubscribe = observer.subscribe(result => {
152+
results.push(result)
153+
})
154+
await sleep(1)
155+
observer.setOptions({
156+
queryKey: key,
157+
queryFn,
158+
select: select2,
159+
})
160+
await sleep(1)
161+
unsubscribe()
162+
expect(count).toBe(2)
163+
expect(results.length).toBe(2)
164+
expect(results[0]).toMatchObject({ data: { myCount: 1 } })
165+
expect(results[1]).toMatchObject({ data: { myCount: 99 } })
166+
})
167+
168+
test('should not run the selector again if the data and selector did not change', async () => {
169+
const key = queryKey()
170+
let count = 0
171+
const results: QueryObserverResult[] = []
172+
const queryFn = () => ({ count: 1 })
173+
const select = (data: ReturnType<typeof queryFn>) => {
174+
count++
175+
return { myCount: data.count }
176+
}
177+
const observer = new QueryObserver(queryClient, {
178+
queryKey: key,
179+
queryFn,
180+
select,
181+
})
182+
const unsubscribe = observer.subscribe(result => {
183+
results.push(result)
184+
})
185+
await sleep(1)
186+
observer.setOptions({
187+
queryKey: key,
188+
queryFn,
189+
select,
190+
})
191+
await sleep(1)
192+
unsubscribe()
193+
expect(count).toBe(1)
194+
expect(results.length).toBe(1)
195+
expect(results[0]).toMatchObject({ data: { myCount: 1 } })
196+
})
197+
133198
test('should not run the selector again if the data did not change', async () => {
134199
const key = queryKey()
135200
let count = 0

0 commit comments

Comments
 (0)