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
436 stars 32 forks source link

is it possible that a client component query can used cached response from server component query? #326

Closed kimvuVarner closed 2 months ago

kimvuVarner commented 2 months ago

Hello!, this is more of a cry for help rather than a issue. But I don't really know where else to ask

I currently have a nextjs + apollograph built upon the pages directory, but want to migrate to the new app directory with app routing. The issue I have is that I don't really find a way to use the ApolloNextAppProvider together with the registerApolloClient. The main issue: In the old way we did a serverside request called useFramework(), that had framework related global states and so on. Then on clientside we used the same request, but now fetched through the apollo-cache so that we didn't get unnecessary requests on.

Now I try to implement this in a very simple POC: this is a server component that fetches the Framework-data, StartPage is a client side component. So everything down the line from StartPage will be clientside. How can i use the same query in a client component in order to use the cached response?

import React from 'react';
import { headers } from 'next/headers';

import { StartPage } from '@pages/sales/StartPage';
import { FrameworkDocument } from '@schema-types';
import { getClient } from '@lib/RSCClient';
const Home = async () => {
  const headersList = headers();
  const domain = headersList.get('host') || '';
  const fullUrl = headersList.get('referer') || '';
  const relativeUrl = fullUrl.split(domain)[1];
  const client = getClient();
  const { data, error } = await client.query({
    query: FrameworkDocument,
    variables: {
      url: relativeUrl,
    },
  });
  return <StartPage data={data} />;
};
export default Home;

I've tried using ApolloNextAppProvider in order to make the client requests work, but then I think I'll get two seperate clients, which I don't want, because the don't share same cache

Are a possible solution to store the framework data in a context? and wrap the context around everything in layout.tsx? or maybe pass data down from the server component?

### Tasks
phryneas commented 2 months ago

I think I'll get two seperate clients, which I don't want, because the don't share same cache

Yes, and that will always be the case - RSC and SSR of Client Components (and later, the browser) run in different processes, sometimes even on different machines at different times. You can share nothing between them, and you should never use the same query in a RSC component and a Client Component. If the cache value updated, the static RSC HTML would not update while the Client Component would update the value - you'd get inconsistent UI.

That's why this is one of the first paragraphs of our README:

❗️ We do handle "RSC" and "SSR" use cases as completely separate. You should generally try not to have overlapping queries between the two, as all queries made in SSR can dynamically update in the browser as the cache updates (e.g. from a mutation or another query), but queries made in RSC will not be updated in the browser - for that purpose, the full page would need to rerender. As a result, any overlapping data would result in inconsistencies in your UI. So decide for yourself, which queries you want to make in RSC and which in SSR, and don't have them overlap.

You can use <PreloadQuery in a RSC context to preload data that you want to later display in a Client component, but you should only use that for preloading - after that, you should consider that data as "client data" and never display it in a RSC component.

In the end, you should decide for every piece of data in your application if you only show it in server components, or only in client components.

kimvuVarner commented 2 months ago

Thanks for the quick response! So never share data between RSC and Client Component only preload.

I have another question regarding the initialization of Apollo client. In our current codebase we have used this guide for our SSR logic: https://medium.com/@zhamdi/server-side-rendering-ssr-using-apollo-and-next-js-ac0b2e3ea461. We have a bunch of function like: initializeApollo(), useApollo(), createApolloClient() and I can't see how they'll fit in the new app router. How do I start to migrate all the apollo-functionality we've built using this guide? Or is this something that isn't necessary anymore? Or is this exactly what <PreloadQuery> does? I know its kind of a big question, and I don't expect to get the whole answer. But this is our goal now: to migrate all this logic into the app directory, so I'll take any tips I get :)

phryneas commented 2 months ago

All of that isn't necessary anymore - it's essentially what this package does (although, due to the new streaming SSR model of the App router in a much more complicated fashion ;) ).

Just follow our README and you will be set up!

kimvuVarner commented 2 months ago

Allright, thanks!

phryneas commented 2 months ago

I believe everything here has been answered, so I'll close the issue :)

If you have any more questions in the future, feel free to open another issue!

github-actions[bot] commented 2 months ago

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Client usage and allow us to serve you better.