-
Notifications
You must be signed in to change notification settings - Fork 36
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
RFC: The Next.js "App Router", React Server Component & "SSR with Suspense" story #9
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you so much for writing! sorry for terse tone — writing from mobile
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much for the thorough review @gaearon!
I've left some more questions for clarification, and will be rewriting some parts of the document according to your explanations and suggestions on monday!
awesome, did another pass to respond. |
and appreciate your weekend time — sorry i didn't get to this earlier. |
First batch of a lot of adjustments. Thanks to @gaearon for the thorough review.
The thing I dislike about the diagrams is they show "SSR Component A", "SSR Component B", "Browser Component A", "Browser Component B" as vertical lines. Vertical lines in this diagram style represent things that have a lifetime – like processes that respond to messages. But components are not processes. In the RSC pass, they have no lifetime at all: a component doesn't "exist". It's just a function call. On the browser, components kinda "exist" but they also don't really "receive messages". The vertical line that would make sense to me is "RSC renderer" and "Client-side React". And "render component A" or "receive HTML for component A" would make sense as "messages" passed to it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aside from parts i don't have context on, lgtm
For now I'm not gonna merge this PR, but link to it from the README. The discussion here might prove very useful for others trying to understand even more nuance. More discussion here is very welcome! |
Absolutely phenomenal work on this! 👏 I've done an initial skim and intend to read it more in depth soon.
I'd like to add a case to this discussion, what about prefetching data in RSCs that is only used in Client Components? One nice thing about RSCs is that they provide a framework-agnostic central way to prefetch. This would rely on de/rehydration of Promises, but does seem like a valid use case for wanting to reuse parts of the cache (even though they haven't finished fetching yet)? Any parts only fetched in Client Components still suffer from client-side request waterfalls unless they are explicitly prefetched, so if we can't prefetch those in RSCs, where do we prefetch them (solvable, but would be cumbersome and different per framework/app)? It would be like using RSCs + Another reason to support it could be for data that is shown both in RSCs and Client Components, but that is never refetched (infinite stale time), or only refetched as part of a RSC refetch. In this case you could pass down a SC to the point where the CC is and show the data that way instead, or pass down the raw value, but this kind of prop drilling might be cumbersome. I'm sure I could come up with other cases (valid or not). I do agree with the original premise that it mostly doesn't make sense though, for the general case. |
This is something that I want to keep exploring, and yes, passing a Promise from the RSC into the Client Component could be very promising here.
Waterfalls are less of a problem if you use fragment composition, so that's generally a pattern that we want to make more popular with Apollo Client.
This one could lead to a bit of a slippery slope, and in the long run probably end up with inconsistencies if developers are not very careful with it - but at the same time I do see some use cases.
Thanks, and please let us know if you have any further thoughts! |
It's nice to hear that this is something you are thinking about too. For React Query we are renaming
That's true! I was stuck thinking about the general case.
Yeah, for the SSR pass this is no more a problem than for RSCs (depending on where the RSC and the SSR servers are located compared to your backend/DB, if RSC is co-located with those and SSR lives on the edge, doing it in SSR would be worse). For client side page transitions though this would lead to the same bad performance as request waterfalls have today. Part of the motivation for RSCs is to "solve" request waterfalls, but they can't do that for data used in Client Components unless we also prefetch that data there, if that makes sense? (Though fragment composition works around that!)
Definitely! My thinking though is that when you consider all of the above things together, and think about how complex this area is to reason about, it would be really nice to provide developers with a single coherent API for prefetching stuff. If we could say "If you are using RSCs, just make sure you prefetch everything in those" and make that work well, that would be golden. Again, I do think fragments are a good alternative solution for this for Apollo (especially considering de/rehydrating promises is pretty alpha), so I'm talking more high level/generally here. 😃 Edit:
Yes, I think there are several ways to support this and help developers avoid ending up with tearing everywhere. It might even be possible to enforce. If you want to prefetch data for the client, pass down a Promise. If you hydrate actual data from an RSC to a Client Component, force "cache-only"/Infinite stale time (possibly with per query opt-out as escape hatch). |
Notable things that have happened since this RFC: Support for
|
The RFC
You can read the rendered RFC here.
The examples
You can find some example applications in the examples folder.
polls-demo
is a simple poll app that showcases both Server Components and Client Components in a single app. It shows the difference between using Apollo Client in Server Components and Client Components.hack-the-supergraph-ssr
example is an example exploring the "SSR and data transporting" angle more in-depth. It also uses the@defer
-related Links discussed in the last part of the RFC.You can change the time deferred responses from the server take by adjusting it in the "Demo Settings" in the top-right corner. The link is configured so that any fragment coming in after 100ms will not be part of SSR, but be fetched on the client. Keep in mind that this uses a real network connection, so there might be some network delay on top of the number you choose here.
app-dir-experiments
is more of an "experimenting around" app I used for building the packagePoints to look out for
next
oftentimes leads to a hydration mismatch. This is due touseId
not matching up between client and server. I assume that this could be a Next.js bug and have filed a bug report here: app dir: useId mismatch on first request innext dev
vercel/next.js#48284We track this in this issue in Known bug: First start in
dev
can lead to a hydration mismatch. #16