@@ -37,6 +37,7 @@ const React = require('react');
37
37
38
38
const { renderToPipeableStream} = require ( 'react-dom/server' ) ;
39
39
const { createFromNodeStream} = require ( 'react-server-dom-webpack/client' ) ;
40
+ const { PassThrough } = require ( 'stream' ) ;
40
41
41
42
const app = express ( ) ;
42
43
@@ -146,34 +147,39 @@ app.all('/', async function (req, res, next) {
146
147
// so we start by consuming the RSC payload. This needs a module
147
148
// map that reverse engineers the client-side path to the SSR path.
148
149
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 ;
166
168
let Root = ( ) => {
167
169
if ( ! cachedResult ) {
168
- cachedResult = getRootAndFormState ( ) ;
170
+ // Read this stream inside the render.
171
+ cachedResult = createFromNodeStream (
172
+ rscResponse2 ,
173
+ ssrManifest
174
+ ) ;
169
175
}
170
- return React . use ( cachedResult ) ;
176
+ return React . use ( cachedResult ) . root ;
171
177
} ;
172
178
// Render it into HTML by resolving the client components
173
179
res . set ( 'Content-type' , 'text/html' ) ;
174
180
const { pipe} = renderToPipeableStream ( React . createElement ( Root ) , {
175
181
bootstrapScripts : mainJSChunks ,
176
- formState : lazyFormState ,
182
+ formState : formState ,
177
183
} ) ;
178
184
pipe ( res ) ;
179
185
} catch ( e ) {
0 commit comments