apollographql / apollo-client-nextjs

Apollo Client support for the Next.js App Router
https://www.npmjs.com/package/@apollo/experimental-nextjs-app-support
MIT License
358 stars 25 forks source link

useSuspenseQuery ignoring cache when page loaded via router.push, <Link> etc #149

Closed tombryden closed 5 months ago

tombryden commented 5 months ago

Hi,

Thanks for this great library.

I have a useSuspenseQuery with next revalidate fetchOptions, it works as expected when refreshing the page, however if you navigate from a different page to the page with the useSuspenseQuery using a Link component (presumably same behaviour will occur with router.push), the query is re-fetched, despite being in the cache.

Hard to give you a reproduction with the suspense query executing in the SSR run so it's difficult to prove this is happening. I have logged to my console in my API when the endpoint is called showing this behaviour is occurring.

Below is the code I have which I thought would work in both scenarios:

   const { data } = useSuspenseQuery(EXAMPLE_QUERY, {
      // cache for one day
      context: {
         fetchOptions: {
            next: {
               revalidate: 86400,
            },
         },
      },
   });

To clarify - to reproduce: 1) Load (refresh) page that doesn't have useSuspenseQuery 2) Click on Link to page with useSuspenseQuery 3) Observe refetch in API despite being cached

Thanks

phryneas commented 5 months ago

I'm not entirely clear - do you mean it is refetching on the server, or on the client?

Normal navigation within your application should not trigger any more SSR after the first page load, so it should not execute on the server. If it does, something seems to be broken with Next.js here - the whole SSR run should not have started in the first place.

tombryden commented 5 months ago

Apologies - I didn't make it very clear.

Refreshing the page so SSR run occurs on the page with useSuspenseQuery causes cache to work as expected (proven via logging in my API - API is not hit when cached).

Using a <Link> from one page to the page with the useSuspenseQuery causes a refetch on the client despite being cached for SSR run. Can provide a demo showing the refetching behaviour if you want.

phryneas commented 5 months ago

Yeah, a little demo would definitely be helpful - thank you :)

tombryden commented 5 months ago

Here you go: https://codesandbox.io/p/github/tombryden/apollo-suspense-cache-bug/main

As mentioned before - I can't demonstrate the SSR run not hitting the API in this demo, but it doesn't after loading & caching the initial data which is good - I have tested with my own API. However to see the behaviour I am questioning:

1) Load the main page (so the data is cached) 2) Load the /another page 3) Click the 'Page' link and observe network tab to see refetch despite cache being available as SSR run works.

After initially loading the main page I would expect the data to be cached and therefore no more fetches of this data should occur until revalidation is due which is set to one day, however navigating from the Link on the /another page causes a refetch of this data.

Let me know if you any further clarification, thanks.

phryneas commented 5 months ago

Hm, here's what I see:

In your demo, when you load /another, you do so by browser navigation - at this point, you start with an empty cache because you have a full page reload. This page is getting SSRed, (and you get the results from the server-side query delivered into your browser cache) because it's the first page you visit in this session. After that, you click the "Page" button, which is a client-side navigation. No SSR happens for that, since it's not the first page you visit. So the cache in the browser (that doesn't have cache for that target page) has to make a request.

Does that explanation make sense to you?

tombryden commented 5 months ago

That makes sense. I was under the assumption (with my little knowledge of caching) that once the data is in the browser cache, even with client-side navigation, this data can be accessed without refetching. If I want this behaviour with client-side navigation, how do you recommend to handle this, is it even possible without using RSC?

I have tested using RSC (using @apollo/experimental-nextjs-app-support/rsc) and the cache works as I expect here (I can navigate using the link from the /another page without causing a refetch).

phryneas commented 5 months ago

It's not possible - Next.js only does a SSR render for the first page you visit, and it would be very counterproductive to copy all cache entries of all pages you might at one point visit over to the browser. Really, it's perfectly okay to just have these requests happening from your browser.

tombryden commented 5 months ago

Many thanks for your clarification :)