From b4477d3800ccb0bdf26670cd1f021d094159c38f Mon Sep 17 00:00:00 2001 From: Hendrik Liebau Date: Thu, 5 Jun 2025 17:19:54 +0200 Subject: [PATCH] [Flight] Add a cached 3rd-party component to the Flight fixture (#33443) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should allow us to visualize what https://github.com/facebook/react/pull/33438 is trying to convey. An uncached 3rd-party component is displayed like this in the dev tools: Screenshot 2025-06-05 at 12 57 32 However, when the component is restored from a cache, it looks like this: Screenshot 2025-06-05 at 12 56 56 The `Server Components ⚛` track is missing completely here, and the `Loading profile...` phase also took way longer than without caching the 3rd-party component. On `main`, the `Server Components ⚛` track is not missing: Screenshot 2025-06-05 at 14 31 20 The cached 3rd-party component starts before the current render, and is also not excluded here, which is of course expected without #33438. --- fixtures/flight/src/App.js | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/fixtures/flight/src/App.js b/fixtures/flight/src/App.js index 7b811dd762..08eaefc90f 100644 --- a/fixtures/flight/src/App.js +++ b/fixtures/flight/src/App.js @@ -1,4 +1,7 @@ import * as React from 'react'; +import {renderToPipeableStream} from 'react-server-dom-webpack/server'; +import {createFromNodeStream} from 'react-server-dom-webpack/client'; +import {PassThrough, Readable} from 'stream'; import Container from './Container.js'; @@ -35,8 +38,50 @@ async function Bar({children}) { return
{children}
; } +async function ThirdPartyComponent() { + return new Promise(resolve => + setTimeout(() => resolve('hello from a 3rd party'), 30) + ); +} + +// Using Web streams for tee'ing convenience here. +let cachedThirdPartyReadableWeb; + +function fetchThirdParty(Component) { + if (cachedThirdPartyReadableWeb) { + const [readableWeb1, readableWeb2] = cachedThirdPartyReadableWeb.tee(); + cachedThirdPartyReadableWeb = readableWeb1; + + return createFromNodeStream(Readable.fromWeb(readableWeb2), { + moduleMap: {}, + moduleLoading: {}, + }); + } + + const stream = renderToPipeableStream( + , + {}, + {environmentName: 'third-party'} + ); + + const readable = new PassThrough(); + // React currently only supports piping to one stream, so we convert, tee, and + // convert back again. + // TODO: Switch to web streams without converting when #33442 has landed. + const [readableWeb1, readableWeb2] = Readable.toWeb(readable).tee(); + cachedThirdPartyReadableWeb = readableWeb1; + const result = createFromNodeStream(Readable.fromWeb(readableWeb2), { + moduleMap: {}, + moduleLoading: {}, + }); + stream.pipe(readable); + + return result; +} + async function ServerComponent() { await new Promise(resolve => setTimeout(() => resolve('deferred text'), 50)); + return await fetchThirdParty(); } export default async function App({prerender}) {