Lazily freeze in case anything in the currently initializing chunk is blocked (#29139)
Fixed #29129. --------- Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
Sebastian Markbåge committed
May 17, 2024 at 13:58 UTC
af3a55e67f0fc9c981b5fe63162b2770ca9548b1
3 files changed
+60
-1
packages/react-client/src/ReactFlightClient.js
+6
-1
@@ -641,7 +641,12 @@ function createElement(
641
}
642
// TODO: We should be freezing the element but currently, we might write into
643
// _debugInfo later. We could move it into _store which remains mutable.
644
- Object.freeze(element.props);
644
+ if (initializingChunkBlockedModel !== null) {
645
+ const freeze = Object.freeze.bind(Object, element.props);
646
+ initializingChunk.then(freeze, freeze);
647
+ } else {
648
+ Object.freeze(element.props);
649
+ }
650
}
651
return element;
652
}
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
+49
@@ -198,6 +198,55 @@ describe('ReactFlightDOMBrowser', () => {
198
});
199
});
200
201
+ it('should resolve client components (with async chunks) when referenced in props', async () => {
202
+ let resolveClientComponentChunk;
203
+
204
+ const ClientOuter = clientExports(function ClientOuter({
205
+ Component,
206
+ children,
207
+ }) {
208
+ return <Component>{children}</Component>;
209
+ });
210
+
211
+ const ClientInner = clientExports(
212
+ function ClientInner({children}) {
213
+ return <span>{children}</span>;
214
+ },
215
+ '42',
216
+ '/test.js',
217
+ new Promise(resolve => (resolveClientComponentChunk = resolve)),
218
+ );
219
+
220
+ function Server() {
221
+ return <ClientOuter Component={ClientInner}>Hello, World!</ClientOuter>;
222
+ }
223
+
224
+ const stream = ReactServerDOMServer.renderToReadableStream(
225
+ <Server />,
226
+ webpackMap,
227
+ );
228
+
229
+ function ClientRoot({response}) {
230
+ return use(response);
231
+ }
232
+
233
+ const response = ReactServerDOMClient.createFromReadableStream(stream);
234
+ const container = document.createElement('div');
235
+ const root = ReactDOMClient.createRoot(container);
236
+
237
+ await act(() => {
238
+ root.render(<ClientRoot response={response} />);
239
+ });
240
+
241
+ expect(container.innerHTML).toBe('');
242
+
243
+ await act(() => {
244
+ resolveClientComponentChunk();
245
+ });
246
+
247
+ expect(container.innerHTML).toBe('<span>Hello, World!</span>');
248
+ });
249
+
250
it('should progressively reveal server components', async () => {
251
let reportedErrors = [];
252
packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js
+5
@@ -69,10 +69,15 @@ exports.clientExports = function clientExports(
69
moduleExports,
70
chunkId,
71
chunkFilename,
72
+ blockOnChunk,
73
) {
74
const chunks = [];
75
if (chunkId) {
76
chunks.push(chunkId, chunkFilename);
77
+
78
+ if (blockOnChunk) {
79
+ webpackChunkMap[chunkId] = blockOnChunk;
80
+ }
81
}
82
const idx = '' + webpackModuleIdx++;
83
webpackClientModules[idx] = moduleExports;