From ef0c6fb467499473975fd2be0af7711793fb636d Mon Sep 17 00:00:00 2001 From: Hendrik Liebau Date: Fri, 17 May 2024 10:46:01 +0200 Subject: [PATCH 1/3] [Flight] Add failing test to reproduce issue with `Object.freeze` --- .../__tests__/ReactFlightDOMBrowser-test.js | 49 +++++++++++++++++++ .../src/__tests__/utils/WebpackMock.js | 5 ++ 2 files changed, 54 insertions(+) diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js index 55ccb44fb041c..d0630287dbf8b 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js @@ -198,6 +198,55 @@ describe('ReactFlightDOMBrowser', () => { }); }); + it('should resolve client components (with async chunks) when referenced in props', async () => { + let resolveClientComponentChunk; + + const ClientOuter = clientExports(function ClientOuter({ + Component, + children, + }) { + return {children}; + }); + + const ClientInner = clientExports( + function ClientInner({children}) { + return {children}; + }, + '42', + '/test.js', + new Promise(resolve => (resolveClientComponentChunk = resolve)), + ); + + function Server() { + return Hello, World!; + } + + const stream = ReactServerDOMServer.renderToReadableStream( + , + webpackMap, + ); + + function ClientRoot({response}) { + return use(response); + } + + const response = ReactServerDOMClient.createFromReadableStream(stream); + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + + await act(() => { + root.render(); + }); + + expect(container.innerHTML).toBe(''); + + await act(() => { + resolveClientComponentChunk(); + }); + + expect(container.innerHTML).toBe('Hello, World!'); + }); + it('should progressively reveal server components', async () => { let reportedErrors = []; diff --git a/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js b/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js index cf21030834438..4527118c1de8b 100644 --- a/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js +++ b/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js @@ -69,10 +69,15 @@ exports.clientExports = function clientExports( moduleExports, chunkId, chunkFilename, + blockOnChunk, ) { const chunks = []; if (chunkId) { chunks.push(chunkId, chunkFilename); + + if (blockOnChunk) { + webpackChunkMap[chunkId] = blockOnChunk; + } } const idx = '' + webpackModuleIdx++; webpackClientModules[idx] = moduleExports; From f31e8e16d34fd5eecaa03b38f5ff3929b0325e51 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Fri, 17 May 2024 10:05:47 -0700 Subject: [PATCH 2/3] Lazily freeze in case anything in the currently initializing chunk is blocked --- packages/react-client/src/ReactFlightClient.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index a31d4a39554c3..0c98b0514bd6c 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -641,7 +641,15 @@ function createElement( } // TODO: We should be freezing the element but currently, we might write into // _debugInfo later. We could move it into _store which remains mutable. - Object.freeze(element.props); + if ( + initializingChunkBlockedModel !== null && + initializingChunkBlockedModel.deps > 0 + ) { + const freeze = Object.freeze.bind(Object, element.props); + initializingChunk.then(freeze, freeze); + } else { + Object.freeze(element.props); + } } return element; } From 9304ef93f4cce2080e9bc70d24a9caf762972d31 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Fri, 17 May 2024 10:24:31 -0700 Subject: [PATCH 3/3] Wait if there's a cycle --- packages/react-client/src/ReactFlightClient.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 0c98b0514bd6c..3129437fa4332 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -641,10 +641,7 @@ function createElement( } // TODO: We should be freezing the element but currently, we might write into // _debugInfo later. We could move it into _store which remains mutable. - if ( - initializingChunkBlockedModel !== null && - initializingChunkBlockedModel.deps > 0 - ) { + if (initializingChunkBlockedModel !== null) { const freeze = Object.freeze.bind(Object, element.props); initializingChunk.then(freeze, freeze); } else {