@samitouri / QOS-React / commits / 3b3decf871

add warning when owner and self are different for string refs (#17864)

When owner and self are different for string refs, we can't easily convert them to callback refs. This PR adds a warning for string refs when owner and self are different to tell users to manually update these refs.

Luna Ruan committed Jan 22, 2020 at 12:32 UTC 3b3decf87121dcc20bfc0820b7b35d2b028adaf2
3 files changed +91 -2
packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.internal.js
+40
@@ -69,4 +69,44 @@ describe('ReactDeprecationWarnings', () => {
69 '\n in Component (at **)',
70 );
71 });
72 +
73 + it('should not warn when owner and self are the same for string refs', () => {
74 + ReactFeatureFlags.warnAboutStringRefs = false;
75 +
76 + class RefComponent extends React.Component {
77 + render() {
78 + return null;
79 + }
80 + }
81 + class Component extends React.Component {
82 + render() {
83 + return <RefComponent ref="refComponent" __self={this} />;
84 + }
85 + }
86 + ReactNoop.renderLegacySyncRoot(<Component />);
87 + expect(Scheduler).toFlushWithoutYielding();
88 + });
89 +
90 + it('should warn when owner and self are different for string refs', () => {
91 + class RefComponent extends React.Component {
92 + render() {
93 + return null;
94 + }
95 + }
96 + class Component extends React.Component {
97 + render() {
98 + return <RefComponent ref="refComponent" __self={{}} />;
99 + }
100 + }
101 +
102 + ReactNoop.render(<Component />);
103 + expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([
104 + 'Warning: Component "Component" contains the string ref "refComponent". ' +
105 + 'Support for string refs will be removed in a future major release. ' +
106 + 'This case cannot be automatically converted to an arrow function. ' +
107 + 'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
108 + 'Learn more about using refs safely here: ' +
109 + 'https://fb.me/react-strict-mode-string-ref',
110 + ]);
111 + });
112 });
packages/react-reconciler/src/ReactChildFiber.js
+11 -1
@@ -116,7 +116,17 @@ function coerceRef(
116 if (__DEV__) {
117 // TODO: Clean this up once we turn on the string ref warning for
118 // everyone, because the strict mode case will no longer be relevant
119 - if (returnFiber.mode & StrictMode || warnAboutStringRefs) {
119 + if (
120 + (returnFiber.mode & StrictMode || warnAboutStringRefs) &&
121 + // We warn in ReactElement.js if owner and self are equal for string refs
122 + // because these cannot be automatically converted to an arrow function
123 + // using a codemod. Therefore, we don't have to warn about string refs again.
124 + !(
125 + element._owner &&
126 + element._self &&
127 + element._owner.stateNode !== element._self
128 + )
129 + ) {
130 const componentName = getComponentName(returnFiber.type) || 'Component';
131 if (!didWarnAboutStringRefs[componentName]) {
132 if (warnAboutStringRefs) {
packages/react/src/ReactElement.js
+40 -1
@@ -5,6 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 +import getComponentName from 'shared/getComponentName';
9 import invariant from 'shared/invariant';
10 import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
11
@@ -19,7 +20,13 @@ const RESERVED_PROPS = {
20 __source: true,
21 };
22
22 -let specialPropKeyWarningShown, specialPropRefWarningShown;
23 +let specialPropKeyWarningShown,
24 + specialPropRefWarningShown,
25 + didWarnAboutStringRefs;
26 +
27 +if (__DEV__) {
28 + didWarnAboutStringRefs = {};
29 +}
30
31 function hasValidRef(config) {
32 if (__DEV__) {
@@ -89,6 +96,33 @@ function defineRefPropWarningGetter(props, displayName) {
96 });
97 }
98
99 +function warnIfStringRefCannotBeAutoConverted(config) {
100 + if (__DEV__) {
101 + if (
102 + typeof config.ref === 'string' &&
103 + ReactCurrentOwner.current &&
104 + config.__self &&
105 + ReactCurrentOwner.current.stateNode !== config.__self
106 + ) {
107 + const componentName = getComponentName(ReactCurrentOwner.current.type);
108 +
109 + if (!didWarnAboutStringRefs[componentName]) {
110 + console.error(
111 + 'Component "%s" contains the string ref "%s". ' +
112 + 'Support for string refs will be removed in a future major release. ' +
113 + 'This case cannot be automatically converted to an arrow function. ' +
114 + 'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
115 + 'Learn more about using refs safely here: ' +
116 + 'https://fb.me/react-strict-mode-string-ref',
117 + getComponentName(ReactCurrentOwner.current.type),
118 + config.ref,
119 + );
120 + didWarnAboutStringRefs[componentName] = true;
121 + }
122 + }
123 + }
124 +}
125 +
126 /**
127 * Factory method to create a new React element. This no longer adheres to
128 * the class pattern, so do not use new to call it. Also, instanceof check
@@ -260,6 +294,7 @@ export function jsxDEV(type, config, maybeKey, source, self) {
294
295 if (hasValidRef(config)) {
296 ref = config.ref;
297 + warnIfStringRefCannotBeAutoConverted(config);
298 }
299
300 // Remaining properties are added to a new props object
@@ -324,6 +359,10 @@ export function createElement(type, config, children) {
359 if (config != null) {
360 if (hasValidRef(config)) {
361 ref = config.ref;
362 +
363 + if (__DEV__) {
364 + warnIfStringRefCannotBeAutoConverted(config);
365 + }
366 }
367 if (hasValidKey(config)) {
368 key = '' + config.key;