Skip to content

Commit

Permalink
fix(core): don't consider queries as enabled if they have no observer…
Browse files Browse the repository at this point in the history
…s and have never fetched (successfully or erroneously)

it's very likely that this used to be a disabled observer; one other case would be canceling a query while you were initially fetching it, but this is a weird corner case that brings all sorts of troubles

additionally, we can check for the queryFn being a skipToken; even if we have data in the cache, when the queryFn is currently set to skipToken, this is means we never want to see this query fetch
  • Loading branch information
TkDodo committed Oct 10, 2024
1 parent e3aca98 commit 0d3c889
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 1 deletion.
40 changes: 40 additions & 0 deletions packages/query-core/src/__tests__/queryClient.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,46 @@ describe('queryClient', () => {
expect(queryFn2).toHaveBeenCalledTimes(2)
})

test('should not refetch disabled inactive queries even if "refetchType" is "all', async () => {
const queryFn = vi
.fn<(...args: Array<unknown>) => string>()
.mockReturnValue('data1')
const observer = new QueryObserver(queryClient, {
queryKey: queryKey(),
queryFn: queryFn,
staleTime: Infinity,
enabled: false,
})
const unsubscribe = observer.subscribe(() => undefined)
unsubscribe()
await queryClient.invalidateQueries({
refetchType: 'all',
})
expect(queryFn).toHaveBeenCalledTimes(0)
})

test('should not refetch inactive queries that have a skipToken queryFn even if "refetchType" is "all', async () => {
const key = queryKey()
const observer = new QueryObserver(queryClient, {
queryKey: key,
queryFn: skipToken,
staleTime: Infinity,
})

queryClient.setQueryData(key, 'data1')

const unsubscribe = observer.subscribe(() => undefined)
unsubscribe()

expect(queryClient.getQueryState(key)?.dataUpdateCount).toBe(1)

await queryClient.invalidateQueries({
refetchType: 'all',
})

expect(queryClient.getQueryState(key)?.dataUpdateCount).toBe(1)
})

test('should cancel ongoing fetches if cancelRefetch option is set (default value)', async () => {
const key = queryKey()
const abortFn = vi.fn()
Expand Down
10 changes: 9 additions & 1 deletion packages/query-core/src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
noop,
replaceData,
resolveEnabled,
skipToken,
timeUntilStale,
} from './utils'
import { notifyManager } from './notifyManager'
Expand Down Expand Up @@ -256,7 +257,14 @@ export class Query<
}

isDisabled(): boolean {
return this.getObserversCount() > 0 && !this.isActive()
if (this.getObserversCount() > 0) {
return !this.isActive()
}
// if a query has no observers, it should still be considered disabled if it never attempted a fetch
return (
this.options.queryFn === skipToken ||
this.state.dataUpdateCount + this.state.errorUpdateCount === 0
)
}

isStale(): boolean {
Expand Down

0 comments on commit 0d3c889

Please sign in to comment.