Skip to content

Commit e3e718e

Browse files
committed
Consume the RSC stream twice in the Flight fixture
1 parent a7144f2 commit e3e718e

File tree

1 file changed

+26
-20
lines changed

1 file changed

+26
-20
lines changed

fixtures/flight/server/global.js

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const React = require('react');
3737

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

4142
const app = express();
4243

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

149-
// This is a bad hack to set the form state after SSR has started. It works
150-
// because we block the root component until we have the form state and
151-
// any form that reads it necessarily will come later. It also only works
152-
// because the formstate type is an object which may change in the future
153-
const lazyFormState = [];
154-
155-
let cachedResult = null;
156-
async function getRootAndFormState() {
157-
const {root, formState} = await createFromNodeStream(
158-
rscResponse,
159-
ssrManifest
160-
);
161-
// We shouldn't be assuming formState is an object type but at the moment
162-
// we have no way of setting the form state from within the render
163-
Object.assign(lazyFormState, formState);
164-
return root;
165-
}
150+
// We need to get the formState before we start rendering but we also
151+
// need to run the Flight client inside the render to get all the preloads.
152+
// The API is ambivalent about what's the right one so we need two for now.
153+
154+
// Tee the response into two streams so that we can do both.
155+
const rscResponse1 = new PassThrough();
156+
const rscResponse2 = new PassThrough();
157+
158+
rscResponse.pipe(rscResponse1);
159+
rscResponse.pipe(rscResponse2);
160+
161+
const {formState} = await createFromNodeStream(
162+
rscResponse1,
163+
ssrManifest
164+
);
165+
rscResponse1.end();
166+
167+
let cachedResult;
166168
let Root = () => {
167169
if (!cachedResult) {
168-
cachedResult = getRootAndFormState();
170+
// Read this stream inside the render.
171+
cachedResult = createFromNodeStream(
172+
rscResponse2,
173+
ssrManifest
174+
);
169175
}
170-
return React.use(cachedResult);
176+
return React.use(cachedResult).root;
171177
};
172178
// Render it into HTML by resolving the client components
173179
res.set('Content-type', 'text/html');
174180
const {pipe} = renderToPipeableStream(React.createElement(Root), {
175181
bootstrapScripts: mainJSChunks,
176-
formState: lazyFormState,
182+
formState: formState,
177183
});
178184
pipe(res);
179185
} catch (e) {

0 commit comments

Comments
 (0)