Skip to content
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

Consume the RSC stream twice in the Flight fixture #28353

Merged
merged 1 commit into from
Feb 16, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions fixtures/flight/server/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const React = require('react');

const {renderToPipeableStream} = require('react-dom/server');
const {createFromNodeStream} = require('react-server-dom-webpack/client');
const {PassThrough} = require('stream');

const app = express();

Expand Down Expand Up @@ -146,34 +147,33 @@ app.all('/', async function (req, res, next) {
// so we start by consuming the RSC payload. This needs a module
// map that reverse engineers the client-side path to the SSR path.

// This is a bad hack to set the form state after SSR has started. It works
// because we block the root component until we have the form state and
// any form that reads it necessarily will come later. It also only works
// because the formstate type is an object which may change in the future
const lazyFormState = [];

let cachedResult = null;
async function getRootAndFormState() {
const {root, formState} = await createFromNodeStream(
rscResponse,
ssrManifest
);
// We shouldn't be assuming formState is an object type but at the moment
// we have no way of setting the form state from within the render
Object.assign(lazyFormState, formState);
return root;
}
// We need to get the formState before we start rendering but we also
// need to run the Flight client inside the render to get all the preloads.
// The API is ambivalent about what's the right one so we need two for now.

// Tee the response into two streams so that we can do both.
const rscResponse1 = new PassThrough();
const rscResponse2 = new PassThrough();

rscResponse.pipe(rscResponse1);
rscResponse.pipe(rscResponse2);

const {formState} = await createFromNodeStream(rscResponse1, ssrManifest);
rscResponse1.end();

let cachedResult;
let Root = () => {
if (!cachedResult) {
cachedResult = getRootAndFormState();
// Read this stream inside the render.
cachedResult = createFromNodeStream(rscResponse2, ssrManifest);
}
return React.use(cachedResult);
return React.use(cachedResult).root;
};
// Render it into HTML by resolving the client components
res.set('Content-type', 'text/html');
const {pipe} = renderToPipeableStream(React.createElement(Root), {
bootstrapScripts: mainJSChunks,
formState: lazyFormState,
formState: formState,
});
pipe(res);
} catch (e) {
Expand Down