Facing an infinite render when a query fails using react-query. useQuery
should only re-fetch if queryKey
changes, or on request failure according to retry
parameter.
Why is queryFn
being re-executed?
Reproducible example: https://stackblitz.com/edit/ilias-react-query-loop?file=src%2Fmain.tsx
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
const useFailingQuery = () =>
useQuery({
queryKey: ['static'],
queryFn() {
console.log('Fetching...');
throw new Error('Fail');
return {};
},
});
const Component = () => {
useFailingQuery();
return <h1>Component</h1>;
};
const App = () => {
const { isLoading } = useFailingQuery();
// This causes infinite query retry
if (isLoading) {
return <span>Loading...</span>;
}
return <Component />;
};
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>
);
In the end, I need to render <Component />
only when useFailingQuery()
is finished loading, and render a page-wide skeleton while it is loading.
2
Answers
While I couldn't figure out the
useQuery()
loop, a workaround is to always render the component:There is a setting called
retryOnMount
(https://tanstack.com/query/v5/docs/framework/react/reference/useQuery#:~:text=retryOnMount%3A%20boolean) that is true by default. This is what is causing your loop. When the<Component />
mounts it triggers a refetch, the refetch makesisLoading
true for a render, which unmounts the component, next render causesisLoading
to be false, which mounts the component again and changes theisLoading
to true.The solution to your problem is setting
retryOnMount
to false in either the global config or the config for the particular query.