diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index c7cb641cd242b..132747e60a883 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -1794,13 +1794,21 @@ function transferReferencedDebugInfo( existingDebugInfo.push.apply(existingDebugInfo, referencedDebugInfo); } } - // We also add it to the initializing chunk since the resolution of that promise is - // also blocked by these. By adding it to both we can track it even if the array/element + // We also add the debug info to the initializing chunk since the resolution of that promise is + // also blocked by the referenced debug info. By adding it to both we can track it even if the array/element // is extracted, or if the root is rendered as is. if (parentChunk !== null) { const parentDebugInfo = parentChunk._debugInfo; - // $FlowFixMe[method-unbinding] - parentDebugInfo.push.apply(parentDebugInfo, referencedDebugInfo); + for (let i = 0; i < referencedDebugInfo.length; ++i) { + const debugInfoEntry = referencedDebugInfo[i]; + if (debugInfoEntry.name != null) { + (debugInfoEntry: ReactComponentInfo); + // We're not transferring Component info since we use Component info + // in Debug info to fill in gaps between Fibers for the parent stack. + } else { + parentDebugInfo.push(debugInfoEntry); + } + } } } } diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 01f37319bf574..3bcf4988feb86 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -2960,13 +2960,6 @@ describe('ReactFlight', () => { { time: 16, }, - { - env: 'third-party', - key: null, - name: 'ThirdPartyAsyncIterableComponent', - props: {}, - stack: ' in Object. (at **)', - }, { time: 16, }, @@ -2995,13 +2988,6 @@ describe('ReactFlight', () => { { time: 19, }, - { - env: 'third-party', - key: null, - name: 'ThirdPartyAsyncIterableComponent', - props: {}, - stack: ' in Object. (at **)', - }, {time: 19}, ] : undefined, @@ -3847,4 +3833,115 @@ describe('ReactFlight', () => { expect(ReactNoop).toMatchRenderedOutput(
not using props
); }); + + // @gate !__DEV__ || enableComponentPerformanceTrack + it('produces correct parent stacks', async () => { + function Container() { + return ReactServer.createElement('div', null); + } + function ContainerParent() { + return ReactServer.createElement(Container, null); + } + function App() { + return ReactServer.createElement( + 'main', + null, + ReactServer.createElement(ContainerParent, null), + ); + } + + const transport = ReactNoopFlightServer.render({ + root: ReactServer.createElement(App, null), + }); + + await act(async () => { + const {root} = await ReactNoopFlightClient.read(transport); + + ReactNoop.render(root); + + expect(root.type).toBe('main'); + if (__DEV__) { + const div = root.props.children; + expect(getDebugInfo(div)).toEqual([ + { + time: 14, + }, + { + env: 'Server', + key: null, + name: 'ContainerParent', + owner: { + env: 'Server', + key: null, + name: 'App', + props: {}, + stack: ' in Object. (at **)', + }, + props: {}, + stack: ' in App (at **)', + }, + { + time: 15, + }, + { + env: 'Server', + key: null, + name: 'Container', + owner: { + env: 'Server', + key: null, + name: 'ContainerParent', + owner: { + env: 'Server', + key: null, + name: 'App', + props: {}, + stack: ' in Object. (at **)', + }, + props: {}, + stack: ' in App (at **)', + }, + props: {}, + stack: ' in ContainerParent (at **)', + }, + { + time: 16, + }, + ]); + expect(getDebugInfo(root)).toEqual([ + { + time: 12, + }, + { + env: 'Server', + key: null, + name: 'App', + props: {}, + stack: ' in Object. (at **)', + }, + { + time: 13, + }, + { + time: 14, + }, + { + time: 15, + }, + { + time: 16, + }, + ]); + } else { + expect(root._debugInfo).toBe(undefined); + expect(root._owner).toBe(undefined); + } + }); + + expect(ReactNoop).toMatchRenderedOutput( +
+
+
, + ); + }); });