Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 31 additions & 13 deletions src/core/queryObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ export class QueryObserver<
private currentQuery!: Query<TQueryFnData, TError, TQueryData>
private currentResult!: QueryObserverResult<TData, TError>
private currentResultState?: QueryState<TQueryData, TError>
private previousOptions?: QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData
>
private previousQueryResult?: QueryObserverResult<TData, TError>
private initialDataUpdateCount: number
private initialErrorUpdateCount: number
Expand Down Expand Up @@ -155,8 +161,7 @@ export class QueryObserver<
setOptions(
options?: QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>
): void {
const prevOptions = this.options

this.previousOptions = this.options
this.options = this.client.defaultQueryObserverOptions(options)

if (
Expand All @@ -168,39 +173,49 @@ export class QueryObserver<

// Keep previous query key if the user does not supply one
if (!this.options.queryKey) {
this.options.queryKey = prevOptions.queryKey
this.options.queryKey = this.previousOptions.queryKey
}

const didUpdateQuery = this.updateQuery()

let optionalFetch
let updateResult
let updateStaleTimeout
let updateRefetchInterval

// If we subscribed to a new query, optionally fetch and update intervals
// If we subscribed to a new query, optionally fetch and update result and timers
if (didUpdateQuery) {
optionalFetch = true
updateResult = true
updateStaleTimeout = true
updateRefetchInterval = true
}

// Optionally fetch if the query became enabled
if (this.options.enabled !== false && prevOptions.enabled === false) {
if (
this.options.enabled !== false &&
this.previousOptions.enabled === false
) {
optionalFetch = true
}

// Update result if the select function changed
if (this.options.select !== this.previousOptions.select) {
updateResult = true
}

// Update stale interval if needed
if (
this.options.enabled !== prevOptions.enabled ||
this.options.staleTime !== prevOptions.staleTime
this.options.enabled !== this.previousOptions.enabled ||
this.options.staleTime !== this.previousOptions.staleTime
) {
updateStaleTimeout = true
}

// Update refetch interval if needed
if (
this.options.enabled !== prevOptions.enabled ||
this.options.refetchInterval !== prevOptions.refetchInterval
this.options.enabled !== this.previousOptions.enabled ||
this.options.refetchInterval !== this.previousOptions.refetchInterval
) {
updateRefetchInterval = true
}
Expand All @@ -212,8 +227,7 @@ export class QueryObserver<
}
}

// Update result when subscribing to a new query
if (didUpdateQuery) {
if (updateResult) {
this.updateResult()
}

Expand Down Expand Up @@ -399,8 +413,12 @@ export class QueryObserver<
}
// Select data if needed
else if (this.options.select && typeof state.data !== 'undefined') {
// Use the previous select result if the query data did not change
if (this.currentResult && state.data === this.currentResultState?.data) {
// Use the previous select result if the query data and select function did not change
if (
this.currentResult &&
state.data === this.currentResultState?.data &&
this.options.select === this.previousOptions?.select
) {
data = this.currentResult.data
} else {
data = this.options.select(state.data)
Expand Down
65 changes: 65 additions & 0 deletions src/core/tests/queryObserver.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,71 @@ describe('queryObserver', () => {
expect(observerResult2.data).toMatchObject({ myCount: 1 })
})

test('should run the selector again if the selector changed', async () => {
const key = queryKey()
let count = 0
const results: QueryObserverResult[] = []
const queryFn = () => ({ count: 1 })
const select1 = (data: ReturnType<typeof queryFn>) => {
count++
return { myCount: data.count }
}
const select2 = (_data: ReturnType<typeof queryFn>) => {
count++
return { myCount: 99 }
}
const observer = new QueryObserver(queryClient, {
queryKey: key,
queryFn,
select: select1,
})
const unsubscribe = observer.subscribe(result => {
results.push(result)
})
await sleep(1)
observer.setOptions({
queryKey: key,
queryFn,
select: select2,
})
await sleep(1)
unsubscribe()
expect(count).toBe(2)
expect(results.length).toBe(2)
expect(results[0]).toMatchObject({ data: { myCount: 1 } })
expect(results[1]).toMatchObject({ data: { myCount: 99 } })
})

test('should not run the selector again if the data and selector did not change', async () => {
const key = queryKey()
let count = 0
const results: QueryObserverResult[] = []
const queryFn = () => ({ count: 1 })
const select = (data: ReturnType<typeof queryFn>) => {
count++
return { myCount: data.count }
}
const observer = new QueryObserver(queryClient, {
queryKey: key,
queryFn,
select,
})
const unsubscribe = observer.subscribe(result => {
results.push(result)
})
await sleep(1)
observer.setOptions({
queryKey: key,
queryFn,
select,
})
await sleep(1)
unsubscribe()
expect(count).toBe(1)
expect(results.length).toBe(1)
expect(results[0]).toMatchObject({ data: { myCount: 1 } })
})

test('should not run the selector again if the data did not change', async () => {
const key = queryKey()
let count = 0
Expand Down