diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index ade358b4186a2..9c3efc544c063 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -902,6 +902,20 @@ function waitForReference( handler.value = parentObject[key]; } + // If the parent object is an unparsed React element tuple and its outlined + // props have now been resolved, we also need to update the props of the + // parsed element object (i.e. handler.value). + if ( + parentObject[0] === REACT_ELEMENT_TYPE && + key === '3' && + typeof handler.value === 'object' && + handler.value !== null && + handler.value.$$typeof === REACT_ELEMENT_TYPE && + handler.value.props === null + ) { + handler.value.props = parentObject[key]; + } + handler.deps--; if (handler.deps === 0) { 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 def347e83a18b..db8edf7ad6831 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js @@ -525,6 +525,104 @@ describe('ReactFlightDOMBrowser', () => { expect(container.innerHTML).toBe('{"foo":1}{"foo":1}'); }); + it('should handle deduped props of re-used elements in fragments (same-chunk reference)', async () => { + let resolveFooClientComponentChunk; + + const FooClient = clientExports( + function Foo({children, item}) { + return children; + }, + '1', + '/foo.js', + new Promise(resolve => (resolveFooClientComponentChunk = resolve)), + ); + + const shared =
; + + function Server() { + return ( + + <>{shared} + + ); + } + + const stream = await serverAct(() => + 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(() => { + resolveFooClientComponentChunk(); + }); + + expect(container.innerHTML).toBe('
'); + }); + + it('should handle deduped props of re-used elements in server components (cross-chunk reference)', async () => { + let resolveFooClientComponentChunk; + + function PassthroughServerComponent({children}) { + return children; + } + + const FooClient = clientExports( + function Foo({children, item}) { + return children; + }, + '1', + '/foo.js', + new Promise(resolve => (resolveFooClientComponentChunk = resolve)), + ); + + const shared =
; + + function Server() { + return ( + + {shared} + + ); + } + + const stream = await serverAct(() => + 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(() => { + resolveFooClientComponentChunk(); + }); + + expect(container.innerHTML).toBe('
'); + }); + it('should progressively reveal server components', async () => { let reportedErrors = [];