Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using fetchPolicy/nextFetchPolicy across server/client rendering in client components #324

Open
awinograd opened this issue Jun 28, 2024 · 3 comments

Comments

@awinograd
Copy link

awinograd commented Jun 28, 2024

Our goal with apollo client is to configure the following behavior:

  1. Always fetch data from network when a component mounts/remounts (to avoid stale data)
  2. Allow using data from the cache while the network fetch occurs
  3. Use data in cache for re-renders

We use the following watchQuery options which implements the above in a fully client-side rendered environment.

        watchQuery: {
          fetchPolicy: 'cache-and-network',
          // We want to rely on the cache when a component re-renders (not remounts) rather
          // than send a new network request on every render after a mutation that touched the cached object
          // https://github.com/apollographql/apollo-client/issues/6760#issuecomment-668188727
          nextFetchPolicy: 'cache-first',
          errorPolicy: 'all',
        },

However, now we're starting to incorporate SSR rendering of client components using useSuspenseQuery.

What I think should happen is that the SSR pass should use fetchPolicy: 'cache-and-network' and then once the client hydrates the first (and subsequent) render(s) would use nextFetchPolicy until the component is unmounted and remounted at which point we'd start back at fetchPolicy.

We observe in practice is a duplicate fetch for each query since one fetch occurs during SSR and the other during CSR. It looks like fetchPolicy/nextFetchPolicy application is not preserved across the server/client boundary. Is this intended behavior? Am I thinking about the implementation of fetchPolicy/nextFetchPolicy correctly? For full transparency I find the docs on fetchPolicy/nextFetchPolicy a bit confusing. It's not totally clear what constitutes an "execution" 🙈

As always, thanks for any help / thoughts you have!

@phryneas
Copy link
Member

phryneas commented Jul 1, 2024

Hmm, this is a complicated mental model - please stay with me :)

When the fetch happens in SSR, two things happen in the browser:

  1. a "network request" is simulated and resolved with the value that same request on the server resolves
  2. the result of that is written to the cache

The reason 1. happens is so that other calls to useSuspenseQuery or useQuery can "latch onto" the network request running on the server.

Now, in the browser, once that component renders, 1. is likely to already have resolved because it has been suspended that time on the server. So, no more "ongoing network request" and the value is already in the cache.

Now, we get to your problem: components in the browser have no knowledge that they rendered on the server before. They just get mounted and behave as such. The cache will already be filled, but you apply 'cache-and-network', so they make a request anyways.

The best you could do here really is to use 'cache-first' - but of course, during the runtime of your application, that will not be satisfying.

I don't really have a good suggestion here, but maybe as a workaround you could have some kind of global recentlyLoaded variable that changes from true to false after 30 seconds, and only after that, your default fetchPolicy changes from cache-first to 'cache-and-network'?

@awinograd
Copy link
Author

@phryneas How do you recommend changing the default fetchPolicy? It doesn't appear that fetchPolicy supports a callback fn like nextFetchPolicy does.

Or are you recommending something like:

  const client = useApolloClient()

  useEffect(() => {
      setTimeout(() => {
          client.defaultOptions.watchQuery.fetchPolicy = 'cache-and-network';
      }, 30000)
  }, [])

@phryneas
Copy link
Member

phryneas commented Jul 1, 2024

Yes, I believe that should work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants