-
Notifications
You must be signed in to change notification settings - Fork 558
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: React Server Components #188
Conversation
89c4746
to
bf51f87
Compare
Will any part of the server-side be tightly coupled to Node.js, or could it run on a different JS runtime (for example, Rhino in Java, embedded V8 like with ClearScript for C#, ChakraCore, etc)? The only mention of Node in the RFC is around debugging ("you would debug your API in Node") but it's unclear as to whether server-side components would be tightly coupled to Node or not. Perhaps this is intentionally an open question at the moment. One of the major benefits and reasons for popularity of JavaScript is that it runs in lots of places, so it would be unfortunate to couple it to one particular implementation. It does say this:
but that's still unclear as to whether "any meta-framework" implicitly means "any Node.js meta-framework" I see a comparison to the older ASP.NET WebForms technology in the RFC, but it'd be interesting to compare it to ASP.NET Blazor too, which is a newer model where the components can either run server-side or client-side (via compilation to WebAssembly). |
From what I read, meta-frameworks refers to other React Frameworks like Next.js and Gatsby |
There are some things I really think will help. As I saw, the following questions came up. Using SSRIf we use SSR, there will be two scenarios:
Without SSRWe send our texts, images, and many pieces of information using JSON. How should we deal with SEO? |
NOTE: edited to provide more context
@Daniel15 React Server Components are conceptually decoupled from the JS runtime, but we also need some amount of environment-specific integration. For example, we currently support a Node.js integration for general consumption (used in the demo) and an internal integration for Relay. One of the main aspects of these integrations is handling the interaction between server/client components and packages (more details on that point in the related RFC). We can consider adding support for other environments (and are interested in the community's feedback about which we may want to prioritize).
@8Brandon Yup, we're referring to frameworks such as Next.js and Gatsby. I'll update the text to clarify. |
@josephsavona other half of that question: how feasible is it for any of this server functionality to work if your backend is not JS-based (ie, Python, Java, Go, etc)? |
Yes, also very interested because this would allow to push for React on many areas where other server tech dominates and cannot be replaced. |
This an interesting area for potential future exploration. As noted in the RFC, one of the benefits of React Server Components is that they allow developers to use a single language and framework to write an application, and share code across the server and client. We can foresee an ecosystem of packages forming around Server Components (libraries designed to be called from Server Components or Shared Components) which would not be available to developers attempting to use aspects of the Server Components architecture from other languages. Similarly, using a different language for the server would likely prohibit (or at least complicate) code sharing. Further, we expect to continue iterating on the response protocol for Server Components ("streaming JSON with slots") and are not yet ready to standardize it, which would be a prerequisite to making interop libraries for other languages. In summary, this is an interesting idea but it feels a bit premature. |
6cb4c00
to
07dd4bc
Compare
Server Components are great idea, it will change the game!
So there will be pretty seamless integration and it will simple to extract heavy logic to server. One question is how to |
heh, translating: "It's still experimental, we're still iterating, we're really focused on doing everything in JS for now for simplicity's sake. We won't rule out it maybe working with other languages, but we're not gonna worry about that for now". Fair summary? |
The "why not use async/await" section of the FAQ IMO needs more detail. I think y'all have made the wrong call here.
A layer to deduplicate fetches sounds great, but that library could use async/await, too. As it stands, libraries like (To be clear, right now the code is using a, uh, very surprising pattern to make asynchronous code appear synchronous: if the result value is cached, it returns the value synchronously, but if not, the fetcher throws a Promise. You know, you'd normally throw exception objects, but JS lets you throw any value, so why not throw a Promise amirite?!? When React catches a Promise (a thenable) it awaits the result, caches it and then re-runs the React component; now the component won't throw a Promise and will run to completion normally.) Using async/await will make this code substantially easier to understand, and the cost of a "tick" is trivial (and worth the price, especially in server components). EDIT: Thinking about this a bit harder, I know the React team has been extremely resistant to async/await in components for years now. Fine. But that needs its own RFC. Some clear written document spelling out in detail why async/await is the wrong approach for React, and not just a comment on this RFC. I'd like to ask the React team to write that RFC doc because I think it can't be written: you'll find that the argument falls apart when you try to explain it. |
@markerikson I appreciate that you're trying to get a clear takeaway. However, there are a lot of considerations here and I worry that trying to distill the complex tradeoffs I alluded to into a short summary may not be helpful to folks. |
My initial thoughts after watching the video are this: All Components in peoples projects can either be thought of as 'Isomorphic' if that codebase is using server-side rendering, OR if that code base is not using serer side rendering then they're all 'client' components. For me personally I feel that introducing an API like this will now cause a abstraction layer where people using server side rendering will now need to think about 3 kinds of components instead of 1 (server/client/isomorphic) and the complexity of thinking about which 'context' you're in because in some contexts you can now only use JSON serializable properties. For me and our team currently the biggest problem we have is scaling React applications which seems to start getting very hard around the (100+ routes) mark, I feel like this RFC although helps performance it hurts scalability as now we need to consider the implementations and find bugs in 3 types of components instead of 1. Would be good to hear other peoples thoughts on this. These are some observations on the API which are purely based on my opinion: BundleSize / Server side code.
// tree shakes out date-fns
ComponentName.getServerSideProps= async (ctx) => {
const { format } = require("date-fns");
return {
formatted: format(new Date(), "M/d/yy")
}
} I will concede that the API you're proposing is more powerful. However the point being made here is that there are already methods out there currently that exist to achieve the majority of this API's functionality in peoples projects. Data Fetching
export const Person(data) {
return <span>{data.name}</span>
}
Person.data = () => api("/api/person")
// and if that Person has a dependency to fetch its data.
export const Person(data) {
return <span>{data.name}</span>
}
Person.data = (id) => api("/api/person", {body: {id: 1}}); Just traverse your React sub-tree from the parent and run all the data methods. Your API is definitely cleaner and I wouldn't recommend to anyone to use the above code, its just there to show that its possible and its definitely possible in other ways as well. XSS
One of the reason I loved hooks is that it solved REAL problems for us that were currently preventing scale in our React applications, where-as I feel like for me personally this doesn't solve real problems I'm currently facing in my React projects. For me personally, I wouldn't trade a fractional increase in performance for the complexity this brings to a project, especially when I have many other levers I can pull before pulling this one. |
I really liked the bundle size reduction for the code that is rendered on the server, specially when it comes to third-party packages that are not doing a great job in code-splitting and tree-shaking, |
I feel this RFC should discuss the nuances of how deployments will need to be handled. Deployments require two versions of the server side code, and two versions of the client side code to all play nicely together. It's relatively common to ensure an API is backwards compatible for one or two deployments before cleaning up the old code. However, what does it mean for a Server Component to be backwards compatible? For example, the heuristics with an API: "adding new fields" is just fine, "removing fields" needs extra care. What are the heuristics for a Server Component? Currently it seems like a size (height or width), scroll-type, or even theme change will be backwards incompatible. |
I read the RFC (thank you for working on making this sort of use case easier and more efficient!). I am very confused by this sort of code: import db from 'db.server';
function Note({id}) {
const note = db.notes.get(id);
return <NoteWithMarkdown note={note} />;
} All the Node APIs are async - so in the above I saw in the video Lauren mentions a fetch API that is "as if it's synchronous so we don't have to wrap it in an effect" - how does that work? |
@ShanonJackson Thanks for raising this point. We considered security from the start when designing this proposal and the streaming protocol guards against injection attacks. I'll add a note to reflect this. |
Actually I installed the experimental version of Interesting. (As a random nit - you're using |
This is interesting concept, however I'm not sure for how large portion of the react community it actually solves any problems. If I understand correctly I can't use this (or I'm limited in using this) when I'm
Considering these points I'm very unlikely to ever use it, so let me ask - how pay to play is this? How aware do existing react packages need to be of this existing? Does it affect performance/bundle size even when I'm not using it? |
Why would that not work? As a side note, I think it's worth exposing (in a |
@benjamingr I may be wrong, but I don't think the server side component can be made observable, so it wouldn't update when property of an object passed in through prop changes. I also believe observability is stripped from object when going through serialization (getters/setter/proxy information is not serializable) so this could break any client components called by the server component as well. |
Is there a plan regarding errors happening mid-stream? Imagine a page that is already 80% rendered but an error occurs in a React component in the last 20% of the HTML stream. Seems like there doesn't seem to be a way to cancel/overwrite already sent HTML (but I didn't dig too much; I may have overlooked a solution). I guess the only way is to have React overwrite the DOM to show the error page. But I wonder how the mechanics would work here. Seems like there need to be some (new?) API between React and the React framework (vite-plugin-ssr, Next.js, ...). There is React Error Boundaries but I wonder how that would work with Server Components. Is it something the React team has already thought about? I'm currrently implementing HTML streaming support for vite-plugin-ssr (shameless plug :-)), and I'm super looking forward to add support for React Server Components. Some prior discussion in the Marko community: marko-js/community#1. |
Perhaps using The result of |
Yes, it's not a perfect example. |
How do you re-render a server-component (say you want to fetch new data) if it cannot be imported by a client-component (who would setState and change the server-component props)? |
Thanks everyone for the comments! We've made a number of changes in response to your feedback:
I've updated this RFC to link to those other RFCs with details. We're going to go forward with this as the first iteration. |
I've added SSR to the original demo: https://github.com/penx/server-components-ssr As we don't want the react-server codemod applied to the ReactDOMServer code, I'm running a separate worker thread with the codemod in it. I guess in production we could build out 2 applications (one with the codemod and one without) making this unnecessary, but it's fairly handy for development purposes! One thing I haven't figured out yet is how to deal with webpack_chunk_load and webpack_require on the server: For these to work in ReactDOMServer I guess they need to be polyfilled? |
I feel like RSC is missing a very important part of the developer journey with React. When we read the RFC, most of the example show how cool RSC is to fetch data directly from the database without the need to create an API in between. However, this is partially true, cause it is only the case on first rendering of the page (at the end very similar to SSR). However, if we want to load a new RSC from a client side component, "it's not really possible". If we follow the recommendation, this would enforce us to still use Being a bit frustrated by those limitation, I got over the recommendation boundary and did the following to use a RSC in a client component: "use client";
import { Suspense, useEffect, useState } from "react";
import { ServerComp2 } from "./ServerComp2";
export const ClientComp = () => {
const [value, setValue] = useState<string | undefined>(undefined);
// Use effect hook to fix Error: Server Functions cannot be called during initial render...
useEffect(() => {
setValue("demo");
}, []);
return (
<div>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
{value !== undefined && (
<Suspense fallback={<div>Loading...</div>}>
<ServerComp2 value={value} />
</Suspense>
)}
</div>
);
}; and then my RSC: "use server";
import { readFile } from "fs/promises";
export const ServerComp2 = async ({ value }: { value: string }) => {
// read tsconfig content
const tsconfig = await readFile("tsconfig.json", "utf8");
return (
<div>
My server component2 with val: {value} <code>{tsconfig}</code>
</div>
);
}; This way, I am able to load a "RSC" using server action, going against the recommendation saying that server action should not be used to fetch data as it doesn't have cache mechanism. |
How to transform an application that is entirely composed of client components into a hybrid application with both client and server components? I hope our application will have better loading speed and performance. |
@YYGod0120 if you are using nextjs have a look at https://nextjs.org/docs/pages/building-your-application/upgrading/app-router-migration or at https://www.youtube.com/watch?v=YQMSietiFm0 Even if in the tutorial they make it look like it is easy, in reality the transition for the SPA/SSR to RSC is not trivial at all, as you will have to completely rethink the architecture of your components... If you are not using a "framework" like nextjs, it will be a big challenge to adopt RSC (at least for the moment). |
Oh, that's unfortunate. My application doesn't use the Next.js. Perhaps I should consider restarting the entire application and creating a version 2.0. |
I read the demo of RSC, still have some questions:
|
In this RFC, we propose introducing Server Components to React. We recommend watching our talk introducing Server Components before reading.
View the formatted RFC