From bef88f7c115536b534f532f8b03c55e3697486c7 Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" Date: Fri, 16 Jan 2026 16:13:29 +0100 Subject: [PATCH 1/2] [DevTools] Stop setting unused global variables (#35532) --- packages/react-devtools-inline/src/backend.js | 36 ++++--------------- .../src/__tests__/setupTests.js | 3 -- packages/react-devtools-shared/src/bridge.js | 2 +- 3 files changed, 8 insertions(+), 33 deletions(-) diff --git a/packages/react-devtools-inline/src/backend.js b/packages/react-devtools-inline/src/backend.js index 849e103f218..2dd03417121 100644 --- a/packages/react-devtools-inline/src/backend.js +++ b/packages/react-devtools-inline/src/backend.js @@ -6,7 +6,10 @@ import {initBackend} from 'react-devtools-shared/src/backend'; import {installHook} from 'react-devtools-shared/src/hook'; import setupNativeStyleEditor from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor'; -import type {BackendBridge} from 'react-devtools-shared/src/bridge'; +import type { + BackendBridge, + SavedPreferencesParams, +} from 'react-devtools-shared/src/bridge'; import type {Wall} from 'react-devtools-shared/src/frontend/types'; import { getIfReloadedAndProfiling, @@ -16,31 +19,14 @@ import { } from 'react-devtools-shared/src/utils'; function startActivation(contentWindow: any, bridge: BackendBridge) { - const onSavedPreferences = (data: $FlowFixMe) => { + const onSavedPreferences = (data: SavedPreferencesParams) => { // This is the only message we're listening for, // so it's safe to cleanup after we've received it. bridge.removeListener('savedPreferences', onSavedPreferences); - const { - appendComponentStack, - breakOnConsoleErrors, - componentFilters, - showInlineWarningsAndErrors, - hideConsoleLogsInStrictMode, - disableSecondConsoleLogDimmingInStrictMode, - } = data; - - contentWindow.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = - appendComponentStack; - contentWindow.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ = - breakOnConsoleErrors; + const {componentFilters} = data; + contentWindow.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters; - contentWindow.__REACT_DEVTOOLS_SHOW_INLINE_WARNINGS_AND_ERRORS__ = - showInlineWarningsAndErrors; - contentWindow.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ = - hideConsoleLogsInStrictMode; - contentWindow.__REACT_DEVTOOLS_DISABLE_SECOND_CONSOLE_LOG_DIMMING_IN_STRICT_MODE__ = - disableSecondConsoleLogDimmingInStrictMode; // TRICKY // The backend entry point may be required in the context of an iframe or the parent window. @@ -49,15 +35,7 @@ function startActivation(contentWindow: any, bridge: BackendBridge) { // Technically we don't need to store them on the contentWindow in this case, // but it doesn't really hurt anything to store them there too. if (contentWindow !== window) { - window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = appendComponentStack; - window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ = breakOnConsoleErrors; window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters; - window.__REACT_DEVTOOLS_SHOW_INLINE_WARNINGS_AND_ERRORS__ = - showInlineWarningsAndErrors; - window.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ = - hideConsoleLogsInStrictMode; - window.__REACT_DEVTOOLS_DISABLE_SECOND_CONSOLE_LOG_DIMMING_IN_STRICT_MODE__ = - disableSecondConsoleLogDimmingInStrictMode; } finishActivation(contentWindow, bridge); diff --git a/packages/react-devtools-shared/src/__tests__/setupTests.js b/packages/react-devtools-shared/src/__tests__/setupTests.js index 6efcefe2f74..5774a573b31 100644 --- a/packages/react-devtools-shared/src/__tests__/setupTests.js +++ b/packages/react-devtools-shared/src/__tests__/setupTests.js @@ -240,9 +240,6 @@ beforeEach(() => { setSavedComponentFilters(getDefaultComponentFilters()); global.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = getDefaultComponentFilters(); - // Also initialize inline warnings so that we can test them. - global.__REACT_DEVTOOLS_SHOW_INLINE_WARNINGS_AND_ERRORS__ = true; - installHook(global, { appendComponentStack: true, breakOnConsoleErrors: false, diff --git a/packages/react-devtools-shared/src/bridge.js b/packages/react-devtools-shared/src/bridge.js index bc2669fda51..af3a87b5968 100644 --- a/packages/react-devtools-shared/src/bridge.js +++ b/packages/react-devtools-shared/src/bridge.js @@ -191,7 +191,7 @@ type NativeStyleEditor_SetValueParams = { value: string, }; -type SavedPreferencesParams = { +export type SavedPreferencesParams = { componentFilters: Array, }; From 6baff7ac763c475087ab5ebf7eef5d0b9f4436df Mon Sep 17 00:00:00 2001 From: Hendrik Liebau Date: Fri, 16 Jan 2026 18:42:09 +0100 Subject: [PATCH 2/2] [Flight] Allow cyclic references to be serialized when unwrapping lazy elements (#35471) When `renderModelDestructive` unwraps a lazy element and subsequently calls `renderModelDestructive` again with the resolved model, we should preserve the parent connection so that cyclic references can be serialized properly. This can occur in an advanced scenario where the result from the Flight Client is serialized again with the Flight Server, e.g. for slicing a precomputed payload into multiple parts. Note: The added test only fails when run with `--prod`. In dev mode, the component info outlining prevents the issue from occurring. --- .../src/__tests__/ReactFlightDOMEdge-test.js | 68 +++++++++++++++++++ .../react-server/src/ReactFlightServer.js | 4 +- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js index 995f0da37ec..940e6c3374f 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js @@ -321,6 +321,74 @@ describe('ReactFlightDOMEdge', () => { expect(result).toEqual('Client Component'); }); + it('should resolve cyclic references in client component props after two rounds of serialization and deserialization', async () => { + const ClientComponent = clientExports(function ClientComponent({data}) { + return ( +
{data.self === data ? 'Cycle resolved' : 'Cycle broken'}
+ ); + }); + const clientModuleMetadata = webpackMap[ClientComponent.$$id]; + const consumerModuleId = 'consumer-' + clientModuleMetadata.id; + const clientReference = Object.defineProperties(ClientComponent, { + $$typeof: {value: Symbol.for('react.client.reference')}, + $$id: {value: ClientComponent.$$id}, + }); + webpackModules[consumerModuleId] = clientReference; + + const cyclic = {self: null}; + cyclic.self = cyclic; + + const stream1 = ReactServerDOMServer.renderToReadableStream( + + + , + webpackMap, + ); + + const promise = ReactServerDOMClient.createFromReadableStream(stream1, { + serverConsumerManifest: { + moduleMap: { + [clientModuleMetadata.id]: { + '*': { + id: consumerModuleId, + chunks: [], + name: '*', + }, + }, + }, + moduleLoading: webpackModuleLoading, + serverModuleMap: null, + }, + }); + + const errors = []; + const stream2 = await serverAct(() => + ReactServerDOMServer.renderToReadableStream(promise, webpackMap, { + onError(error) { + errors.push(error); + }, + }), + ); + + expect(errors).toEqual([]); + + const element = await serverAct(() => + ReactServerDOMClient.createFromReadableStream(stream2, { + serverConsumerManifest: { + moduleMap: null, + moduleLoading: null, + }, + }), + ); + + const ssrStream = await serverAct(() => + ReactDOMServer.renderToReadableStream(element), + ); + const result = await readResult(ssrStream); + + expect(result).toBe('
Cycle resolved
'); + }); + it('should be able to load a server reference on a consuming server if a mapping exists', async () => { function greet(name) { return 'hi, ' + name; diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 021913636bd..1f9bb0a77fa 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -3622,8 +3622,8 @@ function renderModelDestructive( return renderModelDestructive( request, task, - emptyRoot, - '', + parent, + parentPropertyName, resolvedModel, ); }