Is a query not supposed to be generated upon page refresh?
I've observed this behavior where navigation from Page 1 to Page 2 adds the desired queries to the query cache, such that mutations that invalidate the target queries results in the desired refetches.
However, if I refresh Page 2, which calls custom hooks that in turn call useQuery, and then call the mutation method, the queries never appear in the cache to be invalidated, and therefore no refetch happens.
Is this expected behavior? Is there a way to resolve the difference in behavior between navigation and page refresh? Thank you in advance.
The custom hook that implements the useQuery call:
hooks/useGetIndividuals.tsx
The component that employs said hook:
pages/collection/[urlPath]/index.tsx
21 Replies
deep-jade•2y ago
What are you doing with that useState? 😅
metropolitan-bronzeOP•2y ago
The errorMsg one? I’m certainly open to better solutions. Do you think that’s somehow part of the problem?
deep-jade•2y ago
Regarding your issue: have you looked into query devtools? If that does not help, show a code sandbox
metropolitan-bronzeOP•2y ago
have you looked into query devtoolsI have. I can clearly see that there are no queries in the query cache once the page gets reloaded. Which is weird because the page calls useQuery() ... indirectly via a custom hook. I'm wondering whether this means that there's something I don't understand about query + component lifecycles. I've been working on a reproducible example, but can I expect code sandbox to behave the same with respect to page reloads as a normal implementation of a react/next app?
blank-aquamarine•2y ago
yes, while the sandbox is opened in an iframe, you can expand it to open in a new tab
"refreshing" a page is the same as opening a page in a new tab or in a new browser. This shouldn't behave any different than opening the page for the first time
metropolitan-bronzeOP•2y ago
Ok... I've managed to make an example that's similar enough. I'm hoping that it's in the same family of problem as I'm experiencing with my current app. If you go here,
https://stackblitz.com/edit/github-uv9nor-jr6sa4?file=hooks%2FuseUpdateCollection.tsx, Click on the "view" icon in the collections page to visit the NewHotness single-collection page,
Click "Add new video to collection",
type whatever you want in the text input field that appears,
and click, "Add".
You should see in the developer console that it's being very inconsistent when something is in the query cache and when it isn't as well as when it's invalidating. I'm only seeing the invalidation happen successfully sometimes. Any leads on this? Perhaps that will help solve my problem, and if not, it will certainly help me advance the reproducible example. Thanks again in advance for any help!
StackBlitz
Query invalidation minimal example (forked) - StackBlitz
Run official live example code for Next.js Api Routes, created by Vercel on StackBlitz
deep-jade•2y ago
'minimal' 😄
maybe update "react-query": "^3.39.3" to a recent version
and this might be inconsistent because you are not awaiting promises
also: no need to interact with the cache directly... queryClient.getQueryData should be enough
metropolitan-bronzeOP•2y ago
Thank you. I will act on those asap and follow up. Much appreciated.
metropolitan-bronzeOP•2y ago
Ok... I believe that I have updated the example code as advised above: https://stackblitz.com/edit/github-uv9nor-jr6sa4?file=pages%2Fcollection%2F%5BurlPath%5D.tsx
I still can't quite figure out what is wrong. The first time I add a new video, it looks like the queryClient.invalidateQuery() doesn't succeed (by evidence of the identical queryData before and after the invalidation).
The second time I add a new video, there is no longer any queryData at all... and in neither case is the UI updated. Now I'm just really confused.
Mark
StackBlitz
Query invalidation minimal example (forked) - StackBlitz
Run official live example code for Next.js Api Routes, created by Vercel on StackBlitz
deep-jade•2y ago
I’ll try to look into this later. A more minimal example would really help
metropolitan-bronzeOP•2y ago
Thank you. I’ll see what I can pare down in the meantime. But I wanted to two separate pages in order to try to reproduce the behavior I’m seeing on my actual project. While we’re not quite seeing the same behavior in this example, I feel like resolving the issues herein will help me figure out what I’m doing wrong more generally.
metropolitan-bronzeOP•2y ago
Ok. I simplified quite a bit. Hope that this is helpful: https://stackblitz.com/edit/github-uv9nor-pjdfku?file=pages%2Fcollection%2F%5BurlPath%5D.tsx
Mark
StackBlitz
Query invalidation minimal example 5 jan - StackBlitz
Run official live example code for Next.js Api Routes, created by Vercel on StackBlitz
metropolitan-bronzeOP•2y ago
(it's just the two hooks and the one pages/collection/[urlPath].tsx that should matter.)
eastern-cyan•2y ago
I think I know where the problem might be. In _app.tsx, you have the following line.
Nextjs re-renders the _app component during navigation in order to inject the new page component in. When you refresh the page in development, react re-renders everything twice in order to make strict mode work properly (this is in development only). The second re-render in development is only for checks that react performs and is not used for UI updates.
With this, every time the _app component is re-rendered, we are creating a
new QueryClient
, throwing away any previous cached data. In development, the second time that react rerenders _app is only for checks. So, you have a situation where, the latest new query client isn't connected to the UI stuff.
So, the queries used on the initial page refresh are all thrown away without reference. On subsequent navigation, only queries that are used for that page are cached (We create a new query client on every page navigation).
SOLUTION
-----
wrap queryClient
in a memo like below so we use the same instance between re-renders
Make sure you remove all the other new QueryClient()
everywhere except _app.tsx
and just use useQuery()
hook. Let me know if this helps / doesn't help.deep-jade•2y ago
Or even better: useState and using the eslint plugin
metropolitan-bronzeOP•2y ago
Can you clarify what you mean?
Thank you so much for this suggestion! That makes sense. I’ll try and report back.
deep-jade•2y ago
Stable Query Client | TanStack Query Docs
The QueryClient contains the QueryCache, so you'd only want to create one instance of the QueryClient for the lifecycle of your application - not a new instance on every render.
Exception: It's allowed to create a new QueryClient inside an async Server Component, because the async function is only called once on the server.
metropolitan-bronzeOP•2y ago
I see. Thank you!
eastern-cyan•2y ago
This is good stuff. Thanks for pointing out 👍
What's the relation with eslint?
One advantage for using useMemo I find is that you can automatically regenerate the queryClient if it depends on some attributes like API token or some other item.
ratty-blush•2y ago
Instead of creating a new
queryClient
, how about you just clear/reset your current one if your API token changes.
As for the eslint relationship, they're just plugin rules that are recommended to use to catch common mistakes.metropolitan-bronzeOP•2y ago
This totally solved my problem. Thank you so much, everyone!!!