@samitouri / QOS-React / commits / 1beb73de0f

Add flag to test fast jsx (#28816)

Following #28768, add a path to testing Fast JSX on www. We want to measure the impact of Fast JSX and enable a path to testing before string refs are completely removed in www (which is a work in progress). Without `disableStringRefs`, we need to copy any object with a `ref` key so we can pass it through `coerceStringRef()` and copy it into the object. This de-opt path is what is gated behind `enableFastJSXWithStringRefs`. The additional checks should have no perf impact in OSS as the flags remain true there and the build output is not changed. For www, I've benchmarked the addition of the boolean checks with values cached at module scope. There is no significant change observed from our benchmarks and any latency will apply to test and control branches evenly. This added experiment complexity is temporary. We should be able to clean it up, along with the flag checks for `enableRefAsProp` and `disableStringRefs` shortly.

Jack Pope committed May 3, 2024 at 10:47 UTC 1beb73de0f7c3261a0de37620453b102caaa6236
10 files changed +26 -6
packages/react/src/__tests__/ReactJSXRuntime-test.js
+3 -4
@@ -375,9 +375,8 @@ describe('ReactJSXRuntime', () => {
375 expect(didCall).toBe(false);
376 });
377
378 - // @gate enableRefAsProp
379 - // @gate disableStringRefs
380 - it('does not clone props object if key is not spread', async () => {
378 + // @gate enableFastJSX && enableRefAsProp
379 + it('does not clone props object if key and ref is not spread', async () => {
380 const config = {
381 foo: 'foo',
382 bar: 'bar',
@@ -386,7 +385,7 @@ describe('ReactJSXRuntime', () => {
385 const element = __DEV__
386 ? JSXDEVRuntime.jsxDEV('div', config)
387 : JSXRuntime.jsx('div', config);
389 - expect(element.props).toBe(config);
388 + expect(Object.is(element.props, config)).toBe(true);
389
390 const configWithKey = {
391 foo: 'foo',
packages/react/src/jsx/ReactJSXElement.js
+15 -2
@@ -22,6 +22,7 @@ import {
22 enableRefAsProp,
23 disableStringRefs,
24 disableDefaultPropsExceptForClasses,
25 + enableFastJSX,
26 } from 'shared/ReactFeatureFlags';
27 import {checkPropStringCoercion} from 'shared/CheckStringCoercion';
28 import {ClassComponent} from 'react-reconciler/src/ReactWorkTags';
@@ -51,6 +52,10 @@ if (__DEV__) {
52 didWarnAboutElementRef = {};
53 }
54
55 +const enableFastJSXWithStringRefs = enableFastJSX && enableRefAsProp;
56 +const enableFastJSXWithoutStringRefs =
57 + enableFastJSXWithStringRefs && disableStringRefs;
58 +
59 function hasValidRef(config) {
60 if (__DEV__) {
61 if (hasOwnProperty.call(config, 'ref')) {
@@ -355,7 +360,11 @@ export function jsxProd(type, config, maybeKey) {
360 }
361
362 let props;
358 - if (enableRefAsProp && disableStringRefs && !('key' in config)) {
363 + if (
364 + (enableFastJSXWithoutStringRefs ||
365 + (enableFastJSXWithStringRefs && !('ref' in config))) &&
366 + !('key' in config)
367 + ) {
368 // If key was not spread in, we can reuse the original props object. This
369 // only works for `jsx`, not `createElement`, because `jsx` is a compiler
370 // target and the compiler always passes a new object. For `createElement`,
@@ -578,7 +587,11 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
587 }
588
589 let props;
581 - if (enableRefAsProp && disableStringRefs && !('key' in config)) {
590 + if (
591 + (enableFastJSXWithoutStringRefs ||
592 + (enableFastJSXWithStringRefs && !('ref' in config))) &&
593 + !('key' in config)
594 + ) {
595 // If key was not spread in, we can reuse the original props object. This
596 // only works for `jsx`, not `createElement`, because `jsx` is a compiler
597 // target and the compiler always passes a new object. For `createElement`,
packages/shared/ReactFeatureFlags.js
+1
@@ -185,6 +185,7 @@ export const enableInfiniteRenderLoopDetection = true;
185 // during element creation.
186 export const enableRefAsProp = true;
187 export const disableStringRefs = true;
188 +export const enableFastJSX = true;
189
190 // Warn on any usage of ReactTestRenderer
191 export const enableReactTestRendererWarning = true;
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -98,6 +98,7 @@ export const enableServerComponentLogs = true;
98 // because JSX is an extremely hot path.
99 export const enableRefAsProp = false;
100 export const disableStringRefs = false;
101 +export const enableFastJSX = false;
102
103 export const enableReactTestRendererWarning = false;
104 export const disableLegacyMode = false;
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -20,6 +20,7 @@ import typeof * as ExportsType from './ReactFeatureFlags.native-oss';
20 const __TODO_NEXT_RN_MAJOR__ = false;
21 export const enableRefAsProp = __TODO_NEXT_RN_MAJOR__;
22 export const disableStringRefs = __TODO_NEXT_RN_MAJOR__;
23 +export const enableFastJSX = __TODO_NEXT_RN_MAJOR__;
24 export const disableLegacyMode = __TODO_NEXT_RN_MAJOR__;
25 export const disableDOMTestUtils = __TODO_NEXT_RN_MAJOR__;
26 export const useModernStrictMode = __TODO_NEXT_RN_MAJOR__;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -91,6 +91,7 @@ export const renameElementSymbol = true;
91 // const __NEXT_MAJOR__ = __EXPERIMENTAL__;
92 export const enableRefAsProp = true;
93 export const disableStringRefs = true;
94 +export const enableFastJSX = true;
95 export const disableLegacyMode = true;
96 export const disableLegacyContext = true;
97 export const disableDOMTestUtils = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js
+1
@@ -82,6 +82,7 @@ export const enableServerComponentLogs = true;
82
83 export const enableRefAsProp = false;
84 export const disableStringRefs = false;
85 +export const enableFastJSX = false;
86
87 export const enableReactTestRendererWarning = false;
88 export const disableLegacyMode = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -82,6 +82,7 @@ export const enableInfiniteRenderLoopDetection = false;
82
83 export const enableRefAsProp = false;
84 export const disableStringRefs = false;
85 +export const enableFastJSX = false;
86
87 export const enableReactTestRendererWarning = false;
88 export const disableLegacyMode = false;
packages/shared/forks/ReactFeatureFlags.www-dynamic.js
+1
@@ -24,6 +24,7 @@ export const enableDO_NOT_USE_disableStrictPassiveEffect = __VARIANT__;
24 export const enableUseDeferredValueInitialArg = __VARIANT__;
25 export const enableRenderableContext = __VARIANT__;
26 export const enableRefAsProp = __VARIANT__;
27 +export const enableFastJSX = __VARIANT__;
28 export const enableRetryLaneExpiration = __VARIANT__;
29 export const favorSafetyOverHydrationPerf = __VARIANT__;
30 export const disableDefaultPropsExceptForClasses = __VARIANT__;
packages/shared/forks/ReactFeatureFlags.www.js
+1
@@ -36,6 +36,7 @@ export const {
36 disableDefaultPropsExceptForClasses,
37 enableNoCloningMemoCache,
38 enableAddPropertiesFastPath,
39 + enableFastJSX,
40 } = dynamicFeatureFlags;
41
42 // On WWW, __EXPERIMENTAL__ is used for a new modern build.