T
TanStack12mo ago
harsh-harlequin

Tips for debugging over-fetching of data / duplication of http requests?

can anyone share tips / pointers for how to debug duplicated http requests emanating from react-query usage? specific behavior i'm seeing is that a single query invoked via ensureQueryData() is re-run despite having just fetched and returned valid data only a few ticks prior. this results in 2 http requests to the same endpoint with the same parameters returning the same data. as far as i can tell, this is NOT due to react's strict mode mechanism as i see it in production as well. i've been manually inspecting the cache with queryClient.getQueryCache().getAll() but from that perspective, everything should be working as expected (iow, i didn't see any mislabeled cache items or duplicate entries). by default, should calling .ensureQueryData() multiple times result in multiple fetches? if not, are fetches de-duplicated prior to receiving the http response? or is it possible to yield multiple http requests just by calling .ensureQueryData() multiple times prior to receiving the http response? how should i further debug this?
9 Replies
harsh-harlequin
harsh-harlequinOP12mo ago
should calling .ensureQueryData() multiple times result in multiple fetches?
observed behavior suggests not. i tried calling it a whole bunch of times and only one http request was generated. so something else must be going on 🤔
stormy-gold
stormy-gold12mo ago
yes, it should only yield one request. We've seen similar issues on one of our pages, and the issue was that a slightly different queryKey was used. Like one param had filter: '' or so in the key and the other didn't. Our params serializer removed that so the actual network request looked the same, but the key wasn't. You should see in the devtools if you have multiple cache entries or not I want to add a QueryStrictMode that can detect if a query that was prefetched is not used within a given time frame to warn you about such things
harsh-harlequin
harsh-harlequinOP12mo ago
one thing i saw a while back is that a query's enabled key isn't always respected? i'm probably doing something wrong but i have a number of queries like: { enabled: !!(token && userId && jobId), // ensures queryFn won't run until true queryKey: ['users', userId, 'jobs', jobId], queryFn: async () => { return api.jobs.detail(jobId); }, }; i need to verify but i swear i've seen them run when theoretically "disabled" 🤔 i should add: thanks a bunch for taking a look 🙏
stormy-gold
stormy-gold12mo ago
the query devtools should tell you more; generally, it can still fetch if you call refetch() returned from useQuery also queryClient.fetchQuery. enabled: false only disables this useQuery instance (observer)
harsh-harlequin
harsh-harlequinOP12mo ago
query devtools? is that an extension or something? i've just been in the console manually running queryClient.getQueryCache().getAll() but if there way a way to enable logging or similar so i can monitor cache updates, that'd be super-helpful
enabled: false only disables this useQuery instance (observer)
interesting. i'm following the pattern you(?) demonstrated a while back where you integrated react-router and tanstack/query. iirc, the pattern is basically that react-router's "loaders" essentially prefetch data so the scenario i'm guarding against is that the visitor is not signed-in (hence no token) seems to work as i don't see 401s coming from my API 🤔 basically, an SPA built on react-router when i have a series of "loaders" that pre-fetch data then in components i'm using my various useQuery() variants - e.g. useJob() where under the hood it's using useQuery() the enabled field in my queries is there to avoid issuing a bunch of requests before the user is signed-in (iow, i don't have a token)
stormy-gold
stormy-gold12mo ago
Devtools | TanStack Query React Docs
Wave your hands in the air and shout hooray because React Query comes with dedicated devtools! 🥳 When you begin your React Query journey, you'll want these devtools by your side. They help visualize all the inner workings of React Query and will likely save you hours of debugging if you find yourself in a pinch!
harsh-harlequin
harsh-harlequinOP12mo ago
lol using the devtools, my entire site's theme is overridden..? looks kinda cool 🤷‍♂️ here's what i see in the devtools:
2 ["users","foo-oauth2|1234567890","jobs","job_id_1"]
1 ["appStatus"]
1 ["users","foo-oauth2|1234567890","jobs","job_id_1","inputboard"]
1 ["users","foo-oauth2|1234567890","jobs","job_id_1","candidates","candidate_id_1"]
7 ["authuser"]
2 ["users","foo-oauth2|1234567890","jobs","job_id_1"]
1 ["appStatus"]
1 ["users","foo-oauth2|1234567890","jobs","job_id_1","inputboard"]
1 ["users","foo-oauth2|1234567890","jobs","job_id_1","candidates","candidate_id_1"]
7 ["authuser"]
this yields 8 http GET requests: 1. status 2. token 3. job_id_1 4. job_id_1/inputboard 5. job_id_1/candidates/candidate_id_1 6. job_id_1 7. job_id_1/inputboard 8. status in this case, i'm focused on the duplicated job_id_1/* requests but presumably the duplicated status requests are due to the same underlying issue. the duplicate job_id_1/* requests happen some ~200ms after the originals complete. They happen in "dev" and "production" mode implying React's strict mode isn't a factor. They also happen whether you are navigating to that screen (SPA, client-side nav) or hard-refreshing the URL (1st load, no client-side nav in play). i think my keys are formatted properly? they are meant to represent distinct pieces of content within a hierarchy of entities.
harsh-harlequin
harsh-harlequinOP12mo ago
fwiw, this was my guide as i built out my app's data layer: https://tkdodo.eu/blog/react-query-meets-react-router my app is a fairly faithful recreation of that approach only straying to use ensureQueryData() tho i see there's a now note about that too.
React Query meets React Router
React Query and React Router are a match made in heaven.
harsh-harlequin
harsh-harlequinOP12mo ago
lol after all that, it was simply a matter of setting a default staleTime 🤦‍♂️ i forgot the default is 0 which means queries are always refetched. once i set that to 5s, everything worked as expected 🙌 🫠 👍

Did you find this page helpful?