facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
228.23k stars 46.68k forks source link

Bug: Flight (RSC) examples are not accessible #28589

Open kettanaito opened 6 months ago

kettanaito commented 6 months ago

Thank you for featuring a few flight examples on the barebones of RSC! They have been tremendously helpful to me in understanding the model.

That being said, I find myself lost in them. I know it's not an issue per se but I'd be thankful if you consider this feedback and maybe even improve on the examples as the result of it.

Feedback

My feedback comes down to a few random questions and a few fundamental questions to the RSC model and how to get the right image of it in my head. Please note I'm mainly focusing on understanding the internals of RSC (ala I want to build an RSC rendering/serving pipeline from scratch).

Random questions

1. Why do this request forwarding?

https://github.com/facebook/react/blob/9c75cd5e84c4c524f42e65027cfd3dce746d6916/fixtures/flight-esm/server/global.js#L52-L61

Why not split endpoints and have them clear:

Putting it all in a single request handler makes the example significantly harder to grasp. I get it, these are not educational materials but a bit of documentation and structuring would go a long way.

2. Why are "global" and "region" server implementations so dramatically different?

I thought those represented the target of your deployment but they straight changed the approach to rendering completely.

They also run in different contexts:

I get it, they rely on different packages but I fail to see how those implementations co-exist (do they co-exist? Is it like "deploy region.js when building APP_1, and deploy global.js when building APP_2?). Are those two different ways to implement RSC or one inseparable whole split into two parts?

They have to because some actually render the HTML while others just return serialized components from the endpoints. How am I supposed to combine the two? Run two servers...?

3. How does this code compile?

https://github.com/facebook/react/blob/9c75cd5e84c4c524f42e65027cfd3dce746d6916/fixtures/flight-esm/src/index.js#L4

That package has no source on NPM. There's an issue open about it (#27197). An oversight? Would pretty much like to have an ESM version of the binding because others imply I'm using a compiler (webpack/turbopack), which I'm not.

I think the ESM example is actually the one I'm the most interested in because it doesn't need a compiler.

4. Why does only the "region" server of the ESM example import the action?

https://github.com/facebook/react/blob/9c75cd5e84c4c524f42e65027cfd3dce746d6916/fixtures/flight-esm/server/region.js#L57

Is this logic not applicable to other examples, like when using webpack or turobpack? Do the compilers extract and save the actions in the manifest json? A little bit of documentation on this would be fantastic (I will touch on it at the end).

5. How does this even run?

https://github.com/facebook/react/blob/9c75cd5e84c4c524f42e65027cfd3dce746d6916/fixtures/flight-browser/index.html#L23

The bundle it points to contains __webpack_require__ expressions, which will fail in the browser:

https://www.unpkg.com/react-server-dom-webpack@18.3.0-canary-a870b2d54-20240314/umd/react-server-dom-webpack-client.browser.development.js

I feel like this index.html is missing some setup around and does not illustrate the example in isolation (relies on webpack anyway?).

6. Why do examples use babel?

I suspect it's to transform JSX to React.createElement() calls but I'd love to read a bit more about when that transformation happens. I see some examples use a custom --loader for Node.js. Is that related? Do you use that loader to pass imported things through Babel?

This may be the answer to the question I have regarding custom components as those will not render using react-dom or any other packages:

import { renderToPipeableStream } from 'react-dom/server'

function Button() {
  return <button>Hi</button>
}

renderToPipeableStream(React.createElement(Button.type, Button.props, Button.props.children))
// Error!

7. Is there a place for a more developer-friendly API?

Imagine I want to build a tool on top of the RSC protocol. Do you really have to implement those server endpoints by myself? Analyze text/x-component, decode the Server Actions? What happens if React changes how those behave? This looks like it would benefit greatly from a higher-level public API.

Fundamental questions

  1. Can we please collaborate and come up with a series of diagrams that explain the underlying implementation of RSC incrementally? Things like: okay, there are 2/3/4 parts to an RSC:

This would be gold. I'm convinced this will be useful to anybody getting to use RSC. Please, let's do this and not be shy on technical details (that's why I mentioned "incrementally" so anyone can drop off at the level they feel comfortable).

I know @kentcdodds is working on the RSC workshop on EpicWeb.dev and I think he'd be over the moon to have a set of such diagrams to show to his students. I know they'd be tremendously helpful to me as I'm trying to wrap my head around testing all this.

kettanaito commented 6 months ago

From the examples, I can deduce that different DOM bindings behave differently. I suppose it's tied to their nature, such as compiler-based bindings somehow utilize the compiler to help out.

It's interesting, however, that the ESM flight example doesn't need the react-server condition. Does it not mean broken RSC? 🤔 How will it handle "use client"/"user server" in the components? Is that condition simply an environment guard?

If you want to understand the context around my questions, I'd like to build the simplest RSC rendering pipeline possible for testing. This:

const renderer = new Renderer({
  component() {
    return <button>Hello</button>
  }
})
await renderer.serve()

I want it to:

  1. Render my component.
  2. Spawn the actual HTTP server.
  3. Stream its HTML as the initial page HTML from the server.
  4. Embed the right bootstrapModule that hydrates my component so it becomes interactive.
  5. Gives the user control over how to resolve Server Actions as a part of the Renderer API (just having the way to know when the actions happen may be enough).