revalidation RSC Child cause its RSC Parent to render the whole page
Unanswered
endlesslysorrow posted this in #help-forum
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
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
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.
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
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.
https://codesandbox.io/p/devbox/eager-proskuriakova-forked-27z7vg
RSC Parent + RSC Child wrapped in Suspense + RevalidationBtn + FakeSocket + RouterRefreshBtn
I also tried to pass
In addition to that, I tried specifying an immutable key for
<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-23jtkkIn 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
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
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.
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.