Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/reset-queries-refetch-matched.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/query-core': patch
---

Fix `resetQueries` not refetching queries selected by a state-dependent filter (e.g. `predicate: (query) => query.state.status === 'error'`). `resetQueries` reset the matched queries first — mutating their status (`error``pending`) — and then re-ran the same filter to choose refetch targets, so the now-changed queries no longer matched and were never refetched. The matched queries are now snapshotted before the reset and refetched directly.
35 changes: 35 additions & 0 deletions packages/query-core/src/__tests__/queryClient.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1692,6 +1692,41 @@ describe('queryClient', () => {
expect(queryFn2).toHaveBeenCalledTimes(0)
expect(didSkipTokenRun).toBe(false)
})

it('should refetch queries matched by a state-dependent predicate after reset (#10705)', async () => {
const key = queryKey()
let shouldFail = true
const queryFn = vi.fn(() =>
sleep(10).then(() => {
if (shouldFail) throw new Error('failed')
return 'data'
}),
)

const observer = new QueryObserver(queryClient, {
queryKey: key,
queryFn,
retry: false,
})
const unsubscribe = observer.subscribe(() => undefined)

// let the initial fetch settle into an error
await vi.advanceTimersByTimeAsync(10)
expect(queryClient.getQueryState(key)?.status).toBe('error')
expect(queryFn).toHaveBeenCalledTimes(1)

// the refetch triggered by the reset should now succeed
shouldFail = false
const resetPromise = queryClient.resetQueries({
predicate: (query) => query.state.status === 'error',
})
await vi.advanceTimersByTimeAsync(10)
await resetPromise

unsubscribe()
expect(queryFn).toHaveBeenCalledTimes(2)
expect(queryClient.getQueryState(key)?.status).toBe('success')
})
})

describe('focusManager and onlineManager', () => {
Expand Down
12 changes: 10 additions & 2 deletions packages/query-core/src/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,13 +260,21 @@ export class QueryClient {
const queryCache = this.#queryCache

return notifyManager.batch(() => {
queryCache.findAll(filters).forEach((query) => {
// Snapshot the matched queries *before* resetting them. `reset()` mutates
// each query (e.g. status 'error' -> 'pending'), so re-running `filters`
// afterwards could miss them — e.g. a `predicate` filtering on status, see
// #10705. Refetch exactly the queries that matched before the reset.
const queries = queryCache.findAll(filters)

queries.forEach((query) => {
query.reset()
})

const queriesToRefetch = new Set(queries)
return this.refetchQueries(
{
type: 'active',
...filters,
predicate: (query) => queriesToRefetch.has(query),
},
options,
)
Expand Down