From fd0da3eef1e23b7dd5071fef21de414da8e5038e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Thu, 4 Apr 2024 11:20:15 -0400 Subject: [PATCH] Remove _owner field from JSX elements in prod if string refs are disabled (#28739) In prod, the `_owner` field is only used for string refs so if we have string refs disabled, we don't need this field. In fact, that's one of the big benefits of deprecating them. --- packages/jest-react/src/JestReact.js | 10 +++++++++- packages/react-client/src/ReactFlightClient.js | 11 +++++++++++ .../react-noop-renderer/src/createReactNoop.js | 14 +++++++++++++- .../src/__tests__/ReactCreateElement-test.js | 6 +++++- packages/react/src/jsx/ReactJSXElement.js | 17 +++++++++++++++-- packages/shared/ReactElementType.js | 2 +- 6 files changed, 54 insertions(+), 6 deletions(-) diff --git a/packages/jest-react/src/JestReact.js b/packages/jest-react/src/JestReact.js index f94a035f869b0..21307c8393b9d 100644 --- a/packages/jest-react/src/JestReact.js +++ b/packages/jest-react/src/JestReact.js @@ -6,7 +6,7 @@ */ import {REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols'; -import {enableRefAsProp} from 'shared/ReactFeatureFlags'; +import {disableStringRefs, enableRefAsProp} from 'shared/ReactFeatureFlags'; import isArray from 'shared/isArray'; @@ -54,6 +54,14 @@ function createJSXElementForTestComparison(type, props) { value: null, }); return element; + } else if (!__DEV__ && disableStringRefs) { + return { + $$typeof: REACT_ELEMENT_TYPE, + type: type, + key: null, + ref: null, + props: props, + }; } else { return { $$typeof: REACT_ELEMENT_TYPE, diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index b30f9d34d8189..082f94261bfd7 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -38,6 +38,7 @@ import type {Postpone} from 'react/src/ReactPostpone'; import type {TemporaryReferenceSet} from './ReactFlightTemporaryReferences'; import { + disableStringRefs, enableBinaryFlight, enablePostpone, enableRefAsProp, @@ -498,6 +499,16 @@ function createElement( enumerable: false, get: nullRefGetter, }); + } else if (!__DEV__ && disableStringRefs) { + element = ({ + // This tag allows us to uniquely identify this as a React Element + $$typeof: REACT_ELEMENT_TYPE, + + type, + key, + ref: null, + props, + }: any); } else { element = ({ // This tag allows us to uniquely identify this as a React Element diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 03937ee02cfcb..18bd7dbdb01c7 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -32,7 +32,11 @@ import { ConcurrentRoot, LegacyRoot, } from 'react-reconciler/constants'; -import {enableRefAsProp, disableLegacyMode} from 'shared/ReactFeatureFlags'; +import { + enableRefAsProp, + disableLegacyMode, + disableStringRefs, +} from 'shared/ReactFeatureFlags'; type Container = { rootID: string, @@ -799,6 +803,14 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { value: null, }); return element; + } else if (!__DEV__ && disableStringRefs) { + return { + $$typeof: REACT_ELEMENT_TYPE, + type: type, + key: null, + ref: null, + props: props, + }; } else { return { $$typeof: REACT_ELEMENT_TYPE, diff --git a/packages/react/src/__tests__/ReactCreateElement-test.js b/packages/react/src/__tests__/ReactCreateElement-test.js index c4a2d66310492..380f2cd4cf45a 100644 --- a/packages/react/src/__tests__/ReactCreateElement-test.js +++ b/packages/react/src/__tests__/ReactCreateElement-test.js @@ -275,7 +275,11 @@ describe('ReactCreateElement', () => { } const root = ReactDOMClient.createRoot(document.createElement('div')); await act(() => root.render(React.createElement(Wrapper))); - expect(element._owner.stateNode).toBe(instance); + if (__DEV__ || !gate(flags => flags.disableStringRefs)) { + expect(element._owner.stateNode).toBe(instance); + } else { + expect('_owner' in element).toBe(false); + } }); it('merges an additional argument onto the children prop', () => { diff --git a/packages/react/src/jsx/ReactJSXElement.js b/packages/react/src/jsx/ReactJSXElement.js index ffee6e1379706..d20c1d4a25b56 100644 --- a/packages/react/src/jsx/ReactJSXElement.js +++ b/packages/react/src/jsx/ReactJSXElement.js @@ -239,6 +239,19 @@ function ReactElement(type, key, _ref, self, source, owner, props) { value: null, }); } + } else if (!__DEV__ && disableStringRefs) { + // In prod, `ref` is a regular property and _owner doesn't exist. + element = { + // This tag allows us to uniquely identify this as a React Element + $$typeof: REACT_ELEMENT_TYPE, + + // Built-in properties that belong on the element + type, + key, + ref, + + props, + }; } else { // In prod, `ref` is a regular property. It will be removed in a // future release. @@ -774,7 +787,7 @@ export function cloneAndReplaceKey(oldElement, newKey) { enableRefAsProp ? null : oldElement.ref, undefined, undefined, - oldElement._owner, + !__DEV__ && disableStringRefs ? undefined : oldElement._owner, oldElement.props, ); } @@ -800,7 +813,7 @@ export function cloneElement(element, config, children) { let ref = enableRefAsProp ? null : element.ref; // Owner will be preserved, unless ref is overridden - let owner = element._owner; + let owner = !__DEV__ && disableStringRefs ? undefined : element._owner; if (config != null) { if (hasValidRef(config)) { diff --git a/packages/shared/ReactElementType.js b/packages/shared/ReactElementType.js index c6024752a3be6..86b74aa00c1bd 100644 --- a/packages/shared/ReactElementType.js +++ b/packages/shared/ReactElementType.js @@ -13,7 +13,7 @@ export type ReactElement = { key: any, ref: any, props: any, - // ReactFiber + // __DEV__ or for string refs _owner: any, // __DEV__