@samitouri / QOS-React-2 / commits / 5636fad840

[string-refs] log string ref from prod (#31161)

If passed as a feature flag, this calls the configured function when a string ref is used even from prod code to find the last usages.

Jan Kassens committed Oct 10, 2024 at 18:12 UTC 5636fad840942cfea80301d91e931a50c6370d19
9 files changed +55 -20
packages/react/src/jsx/ReactJSXElement.js
+17 -11
@@ -24,6 +24,7 @@ import {
24 disableStringRefs,
25 disableDefaultPropsExceptForClasses,
26 enableOwnerStacks,
27 + enableLogStringRefsProd,
28 } from 'shared/ReactFeatureFlags';
29 import {checkPropStringCoercion} from 'shared/CheckStringCoercion';
30 import {ClassComponent} from 'react-reconciler/src/ReactWorkTags';
@@ -76,7 +77,7 @@ let didWarnAboutStringRefs;
77 let didWarnAboutElementRef;
78 let didWarnAboutOldJSXRuntime;
79
79 -if (__DEV__) {
80 +if (__DEV__ || enableLogStringRefsProd) {
81 didWarnAboutStringRefs = {};
82 didWarnAboutElementRef = {};
83 }
@@ -1314,22 +1315,27 @@ function stringRefAsCallbackRef(stringRef, type, owner, value) {
1315 );
1316 }
1317
1317 - if (__DEV__) {
1318 + if (__DEV__ || enableLogStringRefsProd) {
1319 if (
1320 // Will already warn with "Function components cannot be given refs"
1321 !(typeof type === 'function' && !isReactClass(type))
1322 ) {
1323 const componentName = getComponentNameFromFiber(owner) || 'Component';
1324 if (!didWarnAboutStringRefs[componentName]) {
1324 - console.error(
1325 - 'Component "%s" contains the string ref "%s". Support for string refs ' +
1326 - 'will be removed in a future major release. We recommend using ' +
1327 - 'useRef() or createRef() instead. ' +
1328 - 'Learn more about using refs safely here: ' +
1329 - 'https://react.dev/link/strict-mode-string-ref',
1330 - componentName,
1331 - stringRef,
1332 - );
1325 + if (enableLogStringRefsProd) {
1326 + enableLogStringRefsProd(componentName, stringRef);
1327 + }
1328 + if (__DEV__) {
1329 + console.error(
1330 + 'Component "%s" contains the string ref "%s". Support for string refs ' +
1331 + 'will be removed in a future major release. We recommend using ' +
1332 + 'useRef() or createRef() instead. ' +
1333 + 'Learn more about using refs safely here: ' +
1334 + 'https://react.dev/link/strict-mode-string-ref',
1335 + componentName,
1336 + stringRef,
1337 + );
1338 + }
1339 didWarnAboutStringRefs[componentName] = true;
1340 }
1341 }
packages/shared/ReactFeatureFlags.js
+7
@@ -215,6 +215,13 @@ export const disableClientCache = true;
215 // during element creation.
216 export const enableRefAsProp = true;
217 export const disableStringRefs = true;
218 +/**
219 + * If set to a function, the function will be called with the component name
220 + * and ref string.
221 + *
222 + * NOTE: This happens also in the production build.
223 + */
224 +export const enableLogStringRefsProd: null | ((string, string) => void) = null;
225
226 // Warn on any usage of ReactTestRenderer
227 export const enableReactTestRendererWarning = true;
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -65,6 +65,7 @@ export const enableLazyContextPropagation = true;
65 export const enableLegacyCache = false;
66 export const enableLegacyFBSupport = false;
67 export const enableLegacyHidden = false;
68 +export const enableLogStringRefsProd: null | ((string, string) => void) = null;
69 export const enableNoCloningMemoCache = false;
70 export const enableOwnerStacks = false;
71 export const enablePostpone = false;
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -56,6 +56,7 @@ export const enableContextProfiling = false;
56 export const enableLegacyCache = false;
57 export const enableLegacyFBSupport = false;
58 export const enableLegacyHidden = false;
59 +export const enableLogStringRefsProd: null | ((string, string) => void) = null;
60 export const enableNoCloningMemoCache = false;
61 export const enableObjectFiber = false;
62 export const enableOwnerStacks = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -44,6 +44,7 @@ export const enableUseEffectEventHook = false;
44 export const favorSafetyOverHydrationPerf = true;
45 export const enableComponentStackLocations = true;
46 export const enableLegacyFBSupport = false;
47 +export const enableLogStringRefsProd: null | ((string, string) => void) = null;
48 export const enableFilterEmptyStringAttributesDOM = true;
49 export const enableGetInspectorDataForInstanceInProduction = false;
50 export const enableFabricCompleteRootInCommitPhase = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -46,6 +46,7 @@ export const enableUseEffectEventHook = false;
46 export const favorSafetyOverHydrationPerf = true;
47 export const enableComponentStackLocations = true;
48 export const enableLegacyFBSupport = false;
49 +export const enableLogStringRefsProd: null | ((string, string) => void) = null;
50 export const enableFilterEmptyStringAttributesDOM = true;
51 export const enableGetInspectorDataForInstanceInProduction = false;
52 export const enableRenderableContext = false;
packages/shared/forks/ReactFeatureFlags.www-dynamic.js
+1
@@ -21,6 +21,7 @@ export const disableSchedulerTimeoutInWorkLoop = __VARIANT__;
21 export const enableDeferRootSchedulingToMicrotask = __VARIANT__;
22 export const enableDO_NOT_USE_disableStrictPassiveEffect = __VARIANT__;
23 export const enableHiddenSubtreeInsertionEffectCleanup = __VARIANT__;
24 +export const enableLogStringRefsProd: null | ((string, string) => void) = null;
25 export const enableNoCloningMemoCache = __VARIANT__;
26 export const enableObjectFiber = __VARIANT__;
27 export const enableRenderableContext = __VARIANT__;
packages/shared/forks/ReactFeatureFlags.www.js
+3 -2
@@ -22,20 +22,21 @@ export const {
22 enableDebugTracing,
23 enableDeferRootSchedulingToMicrotask,
24 enableDO_NOT_USE_disableStrictPassiveEffect,
25 + enableHiddenSubtreeInsertionEffectCleanup,
26 enableInfiniteRenderLoopDetection,
27 + enableLogStringRefsProd,
28 enableNoCloningMemoCache,
29 enableObjectFiber,
30 enableRenderableContext,
31 enableRetryLaneExpiration,
32 + enableSiblingPrerendering,
33 enableTransitionTracing,
34 enableTrustedTypesIntegration,
32 - enableHiddenSubtreeInsertionEffectCleanup,
35 favorSafetyOverHydrationPerf,
36 renameElementSymbol,
37 retryLaneExpirationMs,
38 syncLaneExpirationMs,
39 transitionLaneExpirationMs,
38 - enableSiblingPrerendering,
40 } = dynamicFeatureFlags;
41
42 // On WWW, __EXPERIMENTAL__ is used for a new modern build.
scripts/flags/flags.js
+23 -7
@@ -172,7 +172,7 @@ function getNextMajorFlagValue(flag) {
172 const value = ReactFeatureFlagsMajor[flag];
173 if (value === true || value === 'next') {
174 return '✅';
175 - } else if (value === false || value === 'experimental') {
175 + } else if (value === false || value === null || value === 'experimental') {
176 return '❌';
177 } else if (value === 'profile') {
178 return '📊';
@@ -189,7 +189,12 @@ function getOSSCanaryFlagValue(flag) {
189 const value = ReactFeatureFlags[flag];
190 if (value === true) {
191 return '✅';
192 - } else if (value === false || value === 'experimental' || value === 'next') {
192 + } else if (
193 + value === false ||
194 + value === null ||
195 + value === 'experimental' ||
196 + value === 'next'
197 + ) {
198 return '❌';
199 } else if (value === 'profile') {
200 return '📊';
@@ -206,7 +211,7 @@ function getOSSExperimentalFlagValue(flag) {
211 const value = ReactFeatureFlags[flag];
212 if (value === true || value === 'experimental') {
213 return '✅';
209 - } else if (value === false || value === 'next') {
214 + } else if (value === false || value === null || value === 'next') {
215 return '❌';
216 } else if (value === 'profile') {
217 return '📊';
@@ -225,7 +230,7 @@ function getWWWModernFlagValue(flag) {
230 const value = ReactFeatureFlagsWWW[flag];
231 if (value === true || value === 'experimental') {
232 return '✅';
228 - } else if (value === false || value === 'next') {
233 + } else if (value === false || value === null || value === 'next') {
234 return '❌';
235 } else if (value === 'profile') {
236 return '📊';
@@ -244,7 +249,12 @@ function getWWWClassicFlagValue(flag) {
249 const value = ReactFeatureFlagsWWW[flag];
250 if (value === true) {
251 return '✅';
247 - } else if (value === false || value === 'experimental' || value === 'next') {
252 + } else if (
253 + value === false ||
254 + value === null ||
255 + value === 'experimental' ||
256 + value === 'next'
257 + ) {
258 return '❌';
259 } else if (value === 'profile') {
260 return '📊';
@@ -265,7 +275,7 @@ function getRNNextMajorFlagValue(flag) {
275 return '✅';
276 } else if (value === 'next-todo') {
277 return '📋';
268 - } else if (value === false || value === 'experimental') {
278 + } else if (value === false || value === null || value === 'experimental') {
279 return '❌';
280 } else if (value === 'profile') {
281 return '📊';
@@ -286,6 +296,7 @@ function getRNOSSFlagValue(flag) {
296 return '✅';
297 } else if (
298 value === false ||
299 + value === null ||
300 value === 'experimental' ||
301 value === 'next' ||
302 value === 'next-todo'
@@ -308,7 +319,12 @@ function getRNFBFlagValue(flag) {
319 const value = ReactFeatureFlagsNativeFB[flag];
320 if (value === true) {
321 return '✅';
311 - } else if (value === false || value === 'experimental' || value === 'next') {
322 + } else if (
323 + value === false ||
324 + value === null ||
325 + value === 'experimental' ||
326 + value === 'next'
327 + ) {
328 return '❌';
329 } else if (value === 'profile') {
330 return '📊';