Unexpected useQuery behavior
I've been using this excellent library for quite some time, and today I encountered an unexpected behavior.
In my React application, I have a modal window that is rendered conditionally like this:
{isOpen && <Modal />}
Inside the <Modal /> component, I call a useQuery hook with the following options:
useQuery({
queryKey: ['some_string', someId],
queryFn,
staleTime: Infinity,
retryOnMount: true,
throwOnError: false,
});
Here’s the situation:
queryFn throws an error (e.g. HTTP 500).
throwOnError: false ensures the error doesn't propagate to the ErrorBoundary.
The query fails and goes into the error state.
The data is undefined.
Now, when I close the modal and open it again (causing the component to remount), I expect the query to re-run because:
retryOnMount is true
status === 'error'
and data === undefined
However, no request is triggered on remount, even though the query is still in an error state and no data is available.
The query key remains the same: ['some_string', someId].
The only way to make the request fire again is to manually reset the query’s error state in QueryCache.
Is this intended?
Digging into the source code of TanStack Query, it seems that shouldFetchOnMount logic technically allows a refetch in this case when retryOnMount is true, but in practice it doesn't happen.
This behavior was unexpected to me, especially given that:
data is undefined
status is error
and the query is being remounted
It feels like this scenario should trigger a re-fetch automatically.
Am I missing something?
Thanks in advance,
Andrii.
15 Replies
fair-rose•2mo ago
Should definitely fetch again, there must be something else going on. Please show a minimal reproduction, should be quite easy to reproduce if that's really everything there is to it.
xenial-blackOP•2mo ago
Thanks, TkDodo — big fan of your blog! 🙌
I’m running into a situation where I expected useQuery to retry on remount, but it doesn’t — and I’m out of ideas why.
Here’s the setup: inside a modal component, I conditionally run a query like this:
const query = isNotSpinsPage ? offerOnNotSpinsPageQuery : offerQuery;
const { data: offer, isSuccess: isOffer } = useQuery({
...query(offerData.product_id, {
headers: { promo: promo ?? "" },
}),
throwOnError: (error) => throwOnListedErrors(error, [401]),
staleTime: Infinity,
});
In my specific case, query resolves to offerOnNotSpinsPageQuery, which is defined as:
const offerOnNotSpinsPageQuery = (
offerId: number,
options?: Record<string, Record<string, string>>
) =>
({
queryKey: ["offer", String(offerId)],
queryFn: () => fetchOfferOnNotSpinsPage(offerId, options),
}) satisfies FetchQueryOptions;
And the queryFn looks like this:
async function fetchOfferOnNotSpinsPage(
offerId: number,
options?: Record<string, Record<string, string>>
) {
try {
return await apiClient(options)
.get(
profile/products/${offerId})
.json<Offer>();
} catch (error) {
handleCommonFetchErrors(error);
throw error;
}
}
fair-rose•2mo ago
please put it in a stackblitz and replace the fetch calls with
Promise.resolve
xenial-blackOP•2mo ago
Of course, when I reproduce the situation in a simplified setup, everything works as expected 😄
Apparently, I’m missing something — I’ll keep digging into it.
https://stackblitz.com/edit/react-yksfzqm2
Andrii Domanskyi
StackBlitz
useQuery example (duplicated) - StackBlitz
Trying ti recreate hook's strange behavior
fair-rose•2mo ago
That's why making an isolated example is the best move 😉
Now you can add code in the direction of your real situation until it breaks.
xenial-blackOP•2mo ago
Another strange observation — I explicitly set retryOnMount: true (and it’s also true by default), but when I open this query in TanStack Devtools while it’s inactive, it shows retryOnMount: false.
How is that possible?
However, if you pass false to throwOnError instead of a function — everything works!
Memoizing the function passed to throwOnError doesn’t solve the problem.
fair-rose•2mo ago
Why do you set retryOnMount ?
We need to set it to false if you throw errors because of how react works
fair-rose•2mo ago
GitHub
query/packages/react-query/src/errorBoundaryUtils.ts at b5a909528a5...
🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query. - TanStack/query
fair-rose•2mo ago
However
We probably should evaluate the return value of the retryOnMount function in that check
xenial-blackOP•2mo ago
So does that mean that if I pass a function to throwOnError, this check will always return true, even if the function returns false?
In that case, shouldn’t the condition on line 31 actually call the function? Or not?
fair-rose•2mo ago
yeah I tried that, it's not that easy. at this point we don't have the query information yet to pass to the function
again, why do you set
retryOnMount
? It's mostly something that I'd consider "internal". I know, it's documented, but it has a very bad name and the use-case for consumers to change it is quite limited imo
(it has nothing to do with retries btw)
I'm considering moving this flag somewhere else unless there is a real use-case for setting it
people usually set it "by accident" if they want to turn off retries 😅xenial-blackOP•2mo ago
Here’s my case:
In an open modal window, I make a request that is very likely to throw an error. In such cases, I don’t want to throw the error.
The only error I want to react to is an authorization error — 401.
So I want the query to run again on remount for all other errors.
fair-rose•2mo ago
okay, so it fails in the modal, you don't throw, but then it doesn't re-run on a mount again somewhere else?
yeah I can see that happening
quick fix would be for you to reset the query error boundary when the modal unmounts
but yeah, we would need to fix that condition I guess
it's a bit of a chicken-egg problem though
need to think about it
if you have a standalone reproduction, please file an issue
xenial-blackOP•2mo ago
Right now, I’m solving this by manually clearing the error status in the QueryCache.
I was hoping there might be a more elegant way to avoid such a workaround.
In any case, thank you so much, Dominik. I’ve realized the issue isn’t caused by some hidden bug in my code.
You mean — on the React Query GitHub?
Of course, I’ll open an issue — it’ll be an honor!
xenial-blackOP•2mo ago
https://github.com/TanStack/query/issues/9336
@TkDodo 🔮, the issue has been created.
GitHub
useQuery does not perform a retry on mount when the function provid...
Describe the bug If the query options include the following: throwOnError: () => false, retryOnMount: true, staleTime: Infinity, then on remount of the host component, queryFn is not called, eve...