An infinite refetch occurs when an error response is received when using useQueries.
Describe the bug
An infinite refetch occurs when an error response is received when using useQueries. However, when remove the suspense query option, an infinite refresh does not occur. And it is a situation where the Errorboundary cannot catch the error. It can be reproduced by correcting the request url to cause an error in the link below.
Your minimal, reproducible example
https://codesandbox.io/s/angry-diffie-rekf90?file=/src/query.ts:278-577
Steps to reproduce
- Go to example.
- Modify the request url.
- So you can see the infinite refetching.
Expected behavior
When using useQueries, I want the retry and suspense options to work properly and I hope the ErrorBoundary can catch errors well.
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
mac os chrome 108.0.5359.98
Tanstack Query version
v4.19.1
TypeScript version
v4.9.4
Additional context
No response
interesting, thanks for reporting. I wonder why the same doesn't happen for useQuery. I think what's happening is:
- query mounts, fetches, suspends
- query errors, but we don't throw to the error boundary, so query re-mounts
- erroneous queries are always considered stale, and since a component mounts, we fetch and suspend again.
There must be something in useQuery to prevent this that we haven't added to useQueries. I think it has something to do with retryOnMount, but we should set that to false in both cases:
https://github.com/TanStack/query/blob/34ca5581217dae4133046f6cfbc746a22be14dd9/packages/react-query/src/errorBoundaryUtils.ts#L27-L34
@TkDodo Thank you for your quick reply.
I tried many things by referring to your answer, but when I modified the retryOnMount to false, it worked correctly.
The problem seems to be that if the retry option is set to false, retry will not occur when the observer is mounted.
Anyway, your answer was very helpful. Thank you 🙇♂️ 😃
yes, this is exactly what the retryOnMount flag is for. From the docs:
retryOnMount: boolean If set to false, the query will not be retried on mount if it contains an error. Defaults to true.
I still think you, as a user, should not need to set that flag to false for yourself when using suspense. It works for useQuery, so it should also work for useQueries.
I have prepared a failing unit test for this scenario https://github.com/TanStack/query/pull/4882
I already created a wrapper around the useQueries function for better handling types and queries.
By adding retryOnMount: errorResetBoundary.isReset() the infinite loading problem the problem looks fixed and the reset function on the ErrorBoundary does indeed retry the query.
Important: I use suspense: true by default.
/** @version 1.0.0 */
export function useQueries<Qs extends any[]>({ queries }: { queries: readonly [...{ [Key in keyof Qs]: Qs[Key] & GetOptions<Qs[Key]> }] }): QueryResults<Qs>
{
const loads = React.useMemo(() => queries.map(options => Query.prepare(options.queryFn)), [])
const errorResetBoundary = useQueryErrorResetBoundary() // <- get the error boundary QueryErrorResetBoundaryValue
return useQueriesBase({
// @ts-ignore
queries: queries.map(
(options, index) =>
{
const queryKey = options.queryKey
const enabled = areValidArgs(queryKey)
const load = loads[index]
return {
...Query.Options.get(options.queryFn),
retryOnMount: errorResetBoundary.isReset(), // <- this line here
...options,
queryFn: enabled ? load : undefined,
enabled,
queryKey: enabled ? queryKey : undefined,
}
}
),
}) as QueryResults<Qs>
}
So a quick fix for users who use suspense: true could be:
import { useQueries as useQueriesBase, useQueryErrorResetBoundary } from "@tanstack/react-query"
export const useQueries: typeof useQueriesBase = options =>
{
const errorResetBoundary = useQueryErrorResetBoundary()
return useQueriesBase({
...options,
queries: options.queries.map(query => ({ retryOnMount: errorResetBoundary.isReset(), ...query })),
})
}
if someone wants to tackle this one, please do. The failing test case provided by @chekrd should be helpful:
- #4882
@TkDodo I gave this a shot in the dark. There were some clear differences with the order of operations between useQuery and useQueries.
More info in the PR. Let me know your thoughts!