Skip to content

Commit d8a2020

Browse files
committed
add warning when owner and self are different for string refs
1 parent 29b4d07 commit d8a2020

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.internal.js

+40
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,44 @@ describe('ReactDeprecationWarnings', () => {
6969
'\n in Component (at **)',
7070
);
7171
});
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 as 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+
});
72112
});

packages/react-reconciler/src/ReactChildFiber.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,17 @@ function coerceRef(
116116
if (__DEV__) {
117117
// TODO: Clean this up once we turn on the string ref warning for
118118
// 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+
) {
120130
const componentName = getComponentName(returnFiber.type) || 'Component';
121131
if (!didWarnAboutStringRefs[componentName]) {
122132
if (warnAboutStringRefs) {

packages/react/src/ReactElement.js

+40-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8+
import getComponentName from 'shared/getComponentName';
89
import invariant from 'shared/invariant';
910
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
1011

@@ -19,7 +20,13 @@ const RESERVED_PROPS = {
1920
__source: true,
2021
};
2122

22-
let specialPropKeyWarningShown, specialPropRefWarningShown;
23+
let specialPropKeyWarningShown,
24+
specialPropRefWarningShown,
25+
didWarnAboutStringRefs;
26+
27+
if (__DEV__) {
28+
didWarnAboutStringRefs = {};
29+
}
2330

2431
function hasValidRef(config) {
2532
if (__DEV__) {
@@ -89,6 +96,33 @@ function defineRefPropWarningGetter(props, displayName) {
8996
});
9097
}
9198

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 as 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+
92126
/**
93127
* Factory method to create a new React element. This no longer adheres to
94128
* 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) {
260294

261295
if (hasValidRef(config)) {
262296
ref = config.ref;
297+
warnIfStringRefCannotBeAutoConverted(config);
263298
}
264299

265300
// Remaining properties are added to a new props object
@@ -324,6 +359,10 @@ export function createElement(type, config, children) {
324359
if (config != null) {
325360
if (hasValidRef(config)) {
326361
ref = config.ref;
362+
363+
if (__DEV__) {
364+
warnIfStringRefCannotBeAutoConverted(config);
365+
}
327366
}
328367
if (hasValidKey(config)) {
329368
key = '' + config.key;

0 commit comments

Comments
 (0)