Next.js Discord

Discord Forum

revalidation RSC Child cause its RSC Parent to render the whole page

Unanswered
endlesslysorrow posted this in #help-forum
Open in Discord
Question: how to cause revalidation (render on server side RSC Child which placed inside entry-page RSC Parent) without recreation client side?

Exact problem descripted below with the simple codesandboxes, original question was asked on github but no answer was captured after a 5 days

link: https://github.com/vercel/next.js/discussions/66228#discussioncomment-9563654

16 Replies

If I have a server component RSC Parent (entry point page.tsx) in the app-dir which contains another server component - RSC Child with its own data fetching using a tag (this tag is unique to it), and I then revalidate this tag, why does the whole page (the entire entry point) get returned to me? I was expecting the RSC payload to include only the current component. Is this a limitation related to the inability of RSC to handle re-renders? Or is it something wrong with Next.js, where they can't update just the child server component?

As a solution, I've currently decided to move the UI update from cache revalidation to CSR, but maybe I'm missing something and there's a way to revalidate a specific child without refreshing the entire page? Refreshing the whole page is not suitable because there is a socket connection, and reconnecting due to an update of a component unrelated to the socket is unnecessary.

Is it normal for the revalidation of a tag in a child to trigger the re-streaming of the entire page? In my understanding, revalidating a field in the Data Cache by a specific tag should provide me with on-demand rebuilding of the necessary server component, its subsequent streaming, and mounting using React and the RSC payload instructions that come after revalidation.
I tried various compositions, removed unnecessary props being passed from the RSC Parent to the RSC Child, disabling some application functionality as an experiment. I tried wrapping the RSC Child in <Suspense/>, and I tried calling router.refresh from a client component – the result is the same: I get a full reload of the entry page and a socket reconnect.

https://codesandbox.io/p/devbox/eager-proskuriakova-forked-3v5nwp
RSC Parent + RSC Child + RevalidationBtn + FakeSocket + RouterRefreshBtn

In this example, we have the main entry point page, RSC Parent. Within its tree, there's RSC Child, which fetches a dummy API with movies and displays their titles, with the fetch using the tag: "movies".

There are two buttons: one for revalidating the "movies" tag and another for triggering a refresh, which according to the documentation should work as follows: "Refresh the current route. Making a new request to the server, re-fetching data requests, and re-rendering Server Components. The client will merge the updated React Server Component payload without losing unaffected client-side React (e.g. useState) or browser state (e.g. scroll position)."

Instead of a real socket, there's a client component with an effect: it logs "connect" when mounted and "disconnect" when unmounted.

Both tag revalidation and route refresh will remount the client component.

<Suspense/> doesn't help neither here:
https://codesandbox.io/p/devbox/eager-proskuriakova-forked-27z7vg
RSC Parent + RSC Child wrapped in Suspense + RevalidationBtn + FakeSocket + RouterRefreshBtn

I also tried to pass <FakeSocket/> as a slot - either as a prop or as children of the component - the entry point of the page, for example: https://codesandbox.io/p/devbox/215323525-23jtkk

In addition to that, I tried specifying an immutable key for <Suspense/> - the result is the same, I get a full page refresh.
I also tried putting <FakeSocket/> in the layout of the page (entry page). Revalidation of the RSC Child of this page triggers a full render of the page and the layout.

https://codesandbox.io/p/devbox/layout-socket-fyhhtf
@endlesslysorroware you doing revalidatePath?
@Arinji <@296325428526710804>are you doing revalidatePath?
i am trying to revalidate tag
@Arinji <@296325428526710804>are you doing revalidatePath?
you can check any of attached link to sandbox, there are very simple examples of code
Oh my bad but yea revalidate is a very very bad way nextjs uses to like revalidate shit
It will cause an entire rerender of your site
It will invalidate specific caches yes, but will cause a rerender of the entire page
@Arinji It will invalidate specific caches yes, but will cause a rerender of the entire page
is it a next js implementation of RSC restricion or it is about RSC by itself (like React don't support it yet to stream only the selected part?)

it is strange tbh
I think nextjs implementation
@B33fb0n3 any clarifications?
Kk thnx
I did a bit of researching of the Next.js source code, deployed it locally, and added a project from the examples provided above (thanks to the Next.js developers for making a clear guide on how to set it all up). I searched for references related to revalidation for the entry point since the monorepository is HUGE. I set a breakpoint, navigated through the call stack, found the approximate place where the component tree for the entire page is generated, and started reading the code.

Research:

Jump to the action-handler source code: https://github.com/vercel/next.js/blob/8cb8edb686ec8ddf7e24c69545d11175fcb9df02/packages/next/src/server/app-render/action-handler.ts#L789-L794 and see that when we trigger revalidation (via a server action to revalidate a tag from the Data Cache), generateFlight() is called. We notice that there is even a parameter for skipFlight, but more on that later.

Jump to the generateFlight source code: https://github.com/vercel/next.js/blob/f0e4298f674c1cd031ad1e8046f479f7425ffc3b/packages/next/src/server/app-render/app-render.tsx#L301 and see a wonderful comment stating that this function is ONLY used during client-side navigation:

Handle Flight render request. This is only used when client-side navigating. E.g. when you router.push('/dashboard') or router.reload().

Okay, interesting that I have client-side navigation during tag revalidation. I conclude that this is purely a limitation of the current implementation of Next.js's app-render. By the way, I tried passing skipFlight: true just for experiments. The page still recompiled.