-
-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
invalidateQueries
inconsistently cancels fetchQuery
#8060
Comments
the sandbox seems to be private |
Apologies. Should be public now. |
The issue seems to be specific to The Cancellation itself is on purpose, as calling You could also work around it by passing
I'd have to dig deeper to understand why the cancel error is only thrown if there is an active observer 🤔 . I generally agree that it should consistently reject.
The problem is that we can't just catch the error in
That's too big a change, as the only way to get an observer is by creating a so I guess we'll have to look into option A :) |
Thanks for your time.
From my understanding, invalidation doesn't cancel fetches by itself, but it might trigger a revalidation of active queries. The revalidation always cancels the previous So the question is: is there any reason why invalidation should not always cancel any ongoing fetch. If there isn't, perhaps we could add invalidateQueries(
filters: InvalidateQueryFilters = {},
options: InvalidateOptions = {},
): Promise<void> {
return notifyManager.batch(() => {
this.#queryCache.findAll(filters).forEach((query) => {
query.invalidate()
+ query.cancel({ silent: true })
})
if (filters.refetchType === 'none') {
return Promise.resolve()
} |
A few more thoughts:
Could we perhaps implement Option C by catching a CanceledError in Naive approach: fetchQuery<
TQueryFnData,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
TPageParam = never,
>(
options: FetchQueryOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
): Promise<TData> {
const defaultedOptions = this.defaultQueryOptions(options)
// https://github.com/tannerlinsley/react-query/issues/652
if (defaultedOptions.retry === undefined) {
defaultedOptions.retry = false
}
const query = this.#queryCache.build(this, defaultedOptions)
return query.isStaleByTime(
resolveStaleTime(defaultedOptions.staleTime, query),
)
- ? query.fetch(defaultedOptions)
+ ? query.fetch(defaultedOptions).catch((e) => {
+ if (isCancelledError(e)) return query.fetch(defaultedOptions, { cancelRefetch: false })
+ else throw e
+ })
: Promise.resolve(query.state.data as TData)
} The above solution doesn't cover multiple scenarios:
But in principle something like that might work without a major change. |
I've been playing around with this issue a bit, as part of which I redid the react router example with tanstack router: https://stackblitz.com/edit/tanstack-router-invalidated-cancel-error A few differences from @SebKranz rr example:
|
Describe the bug
fetchQuery
may throw anCancelledError { silent: true, revalidate: undefined }
under the following conditions:active
state. For example because a component withuseQuery
is still mounted.In the real world, this might happen in combination with
react-router
, when the user submits a form and then navigates to a new (or the same) page. In my case, we were updating a search param to close a modal after a form submission, which then triggered the loader function containing afetchQuery
call. We're also using a mutation observer1 to trigger invalidations, which is why the invalidation happened only after the navigation began.I fixed the issue by using
ensureQueryData
instead offetchQuery
. This is the better choice anyways.Still, this seems like a footgun since it is difficult to catch while testing. Further, if the intended behavior for
fetchQuery
is to throw aCancelledError
on invalidation, it should always do that and not only when a revalidation is triggered.Your minimal, reproducible example
https://codesandbox.io/p/sandbox/react-query-cancellederror-on-invalidation-d4g259
Steps to reproduce
See steps above
Expected behavior
Option A: The returned promise should consistently reject, regardless of whether an unrelated observer exists.
Option B: The returned promise should consistently resolve with the data returned from this fetch
Option C: The QueryClient should treat pending promises as observers, revalidate automatically and always resolve with the result from the latest revalidation.
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
Tanstack Query adapter
react-query
TanStack Query version
5.50.1
TypeScript version
No response
Additional context
No response
Footnotes
inspired by https://tkdodo.eu/blog/automatic-query-invalidation-after-mutations ↩
The text was updated successfully, but these errors were encountered: