-
Notifications
You must be signed in to change notification settings - Fork 46.9k
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
[Fizz] Implement renderDocument
#25970
Conversation
bb08b6d
to
65cf1fa
Compare
packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js
Outdated
Show resolved
Hide resolved
6309c30
to
74de63c
Compare
5abeef7
to
308599c
Compare
## Summary Should unblock #25970 If the callback for `toWarnDev` was `async` and threw, we didn't ultimately reject the await Promise from the matcher. This resulted in tests failing even though the failure was expected due to a test gate. ## How did you test this change? - [x] tested in #25970 with `yarn test --r=stable --env=development packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js --watch` - [x] `yarn test` - [x] CI
## Summary Should unblock #25970 If the callback for `toWarnDev` was `async` and threw, we didn't ultimately reject the await Promise from the matcher. This resulted in tests failing even though the failure was expected due to a test gate. ## How did you test this change? - [x] tested in #25970 with `yarn test --r=stable --env=development packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js --watch` - [x] `yarn test` - [x] CI DiffTrain build for [4f8ffec](4f8ffec) [View git log for this commit](https://github.com/facebook/react/commits/4f8ffec453c41fcb6e98cb4e003f7319bb1c81b9)
a835230
to
7373264
Compare
renderIntoContainer allows you to stream react into a specific DOM node identified by a element ID. It returns a ReadableStream but differs from renderToReadableStream in that you get access to the stream syncronously. Since the top level Component gets inserted like a boundary I am calling what would normally be the "Shell" of a React app the Root Boundary when using renderIntoContainer. While the top level Components stream in like a boundary there is not actually a Suspense Boundary wrapper. However it will act like one in that stylesheets will be loaded before the content is revealed in the container Node. If stylehseet loading fails hydration will fall back to client rendering It allows the passing of fallback bootstrap scripts which are used if the Root Boundary errors.
7373264
to
45a1577
Compare
Nit: I get that If you just consider the SSR document it's more like you render the "whole" document.
|
@@ -109,6 +109,7 @@ export const useModernStrictMode = false; | |||
export const enableFizzExternalRuntime = true; | |||
|
|||
export const enableFizzIntoContainer = __EXPERIMENTAL__; | |||
export const enableFizzIntoDocument = __EXPERIMENTAL__; |
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.
Same as the other one, might as well turn this one on since its use is optional.
@@ -87,6 +87,7 @@ export const useModernStrictMode = false; | |||
export const enableFizzExternalRuntime = false; | |||
|
|||
export const enableFizzIntoContainer = false; | |||
export const enableFizzIntoDocument = false; |
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.
Turn the flag on in anything that's not expected to use it.
@@ -123,6 +123,9 @@ const DataStreamingFormat: StreamingFormat = 1; | |||
export type ResponseState = { | |||
bootstrapChunks: Array<Chunk | PrecomputedChunk>, | |||
fallbackBootstrapChunks: void | Array<Chunk | PrecomputedChunk>, | |||
requiresEmbedding: boolean, | |||
hasHead: boolean, | |||
hasHtml: boolean, |
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.
Sometimes V8 will deopt objects with more than 16 properties or something like that. It's probably time to consolidate these to a bitmask.
Same thing with sentXFunction can probably just be a bitmask of which ones have been sent.
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.
I've force pushed a number of updates since this was in the commits. I think I sent you a commit range but that has been outdated for a while. I worry you probably reviewed a quite outdated version of the implemenation. For this bit of feedback in particular I already updated to a bitmask. I could consolidate further since I'm not using very many of the bits
@@ -2301,18 +2305,15 @@ function flushCompletedQueues( | |||
if (request.pendingRootTasks === 0) { | |||
if (enableFloat) { | |||
const preamble = request.preamble; | |||
for (i = 0; i < preamble.length; i++) { | |||
// we expect the preamble to be tiny and will ignore backpressure |
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.
This comment is still relevant.
@@ -2301,18 +2305,15 @@ function flushCompletedQueues( | |||
if (request.pendingRootTasks === 0) { | |||
if (enableFloat) { | |||
const preamble = request.preamble; |
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.
It's probably time to rethink the concept of request.preamble
. It was an elegant way to solve this problem before, but now it's way more complicated so it's not elegant and just unnecessary. The information is encoded on the responseState anyway.
let includedAttributeProps = false; | ||
|
||
if (!responseState.hasHead) { | ||
responseState.hasHead = true; |
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.
<head>
should not allow any attributes in this mode. It's too flaky in case we flush the preamble before it's discovered so it would have different semantics in different cases depending on data. So it should never have attributes to avoid you accidentally relying on it.
In other words, this can all be much more simplified because in this mode, you never write head when pushing.
You can always write head when you're writing the HTML tag. Whether it's implicit or not.
That means that the whole thing gets much more simplified because the whole preamble is just a boolean. Whether you have html+head or not.
…in instruction writing
This commit adds the function renderIntoDocument in react-dom/server and adds the ability to embed the rendered children in the necessary html tags to repereset a full document. this means you can render "<html>...</html>" or "<div>...</div>" and either way the render will emit html, head, and body tags as necessary to describe a valid and complete HTML page. Like renderIntoContainer, renderIntoDocument provides a stream immediately. While there is a shell of sorts this fucntion will start writing content from the preamble (html and head tags, plus resources that flush in the head) before finishing the shell. Additionally renderIntoContainer accepts fallback children and fallback bootstrap script options. If the Shell errors the fallback children will render instead of children. The expectation is that the client will attempt to render fresh on the client.
45a1577
to
3ec1561
Compare
I updated to |
renderIntoDocument
renderDocument
af13a1c
to
15e4c45
Compare
With the implementation of |
## Summary Should unblock facebook/react#25970 If the callback for `toWarnDev` was `async` and threw, we didn't ultimately reject the await Promise from the matcher. This resulted in tests failing even though the failure was expected due to a test gate. ## How did you test this change? - [x] tested in facebook/react#25970 with `yarn test --r=stable --env=development packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js --watch` - [x] `yarn test` - [x] CI DiffTrain build for [4f8ffec453c41fcb6e98cb4e003f7319bb1c81b9](facebook/react@4f8ffec) [View git log for this commit](https://github.com/facebook/react/commits/4f8ffec453c41fcb6e98cb4e003f7319bb1c81b9)
stacked on #25703
renderDocument
andrenderDocumentAsPipeableStream
This PR adds a new rendering mode for rendering an entire Document on the server.
Example
On the server use
renderDocument
and stream the results to the clientClient receives if the Shell does not error for the primary children
Client receives the following if the Shell does error for the primary children
Primary Characteristics
There are three primary distinguishing characteristics with the
renderDocument
implementations that explain why existing functions such asrenderToReadableStream
were not suitablerenderDocument
is given in a full HTML document alleviates this limitationrenderToReadableStream
does not provide you with the stream until the shell has finished.fallback
that will render if the Shell forchildren
errors. While other methods don't flush anything until the Shell finishes and thus you can spin up a separate render call, or handle the error case with a different status code and response content therenderDocument
function likely already sent bytes before the Shell has errored so it must coordinate the fallback state more holistically.Resource Flushing Semantics
Some Resources can flush even before the Shell is finished, including preloads, and scripts. Stylesheets for the first precedence can be flushed as well and we can send preloads for stylesheeets for other precedences. The reason we cannot flush all stylesheets of any precedence early is we need the order to be guaranteed and we don't do any re-ordering on the client so they need to leave the server in the right order unless they are delivered as part of a Suspense Boundary reveal.
Ancillary PR note
A related incidental change accompanies this PR for the other render methods. If we render an
<html>
tag but do not render a<head>
tag, we will emit an<head>
tag into the stream just after<html>
and before any other rendered content (usually a<body>
but you can have non-spec html). It is possible this tag won't be empty because any Resources that get hoisted to the head will emit there. Currently these tags emit after the html tag and before any rendered content which may work based on permissive Browser HTML parsing rules but is not valid HTML. One might try to argue this is a breaking change but relying on there NOT being a<head>
is not practically possible given the Browser invents a DOM node for this head anyway