Skip to content

Commit

Permalink
feat: run selector if reference changes to enable use of dependencies (
Browse files Browse the repository at this point in the history
  • Loading branch information
boschni committed Feb 6, 2021
1 parent 7c08f33 commit bd6c2b5
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 13 deletions.
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

1 comment on commit bd6c2b5

@vercel
Copy link

@vercel vercel bot commented on bd6c2b5 Feb 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.