Skip to content

Commit 6cb196b

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

File tree

3 files changed

+94
-2
lines changed

3 files changed

+94
-2
lines changed

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,50 @@ 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+
class RefComponent extends React.Component {
75+
render() {
76+
return null;
77+
}
78+
}
79+
class Component extends React.Component {
80+
render() {
81+
return <RefComponent ref="refComponent" __self={this} />;
82+
}
83+
}
84+
85+
ReactNoop.render(<Component />);
86+
expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([
87+
'Warning: Component "Component" contains the string ref "refComponent". ' +
88+
'Support for string refs will be removed in a future major release. ' +
89+
'We recommend using useRef() or createRef() instead. ' +
90+
'Learn more about using refs safely here: ' +
91+
'https://fb.me/react-strict-mode-string-ref' +
92+
'\n in Component (at **)',
93+
]);
94+
});
95+
96+
it('should warn when owner and self are different for string refs', () => {
97+
class RefComponent extends React.Component {
98+
render() {
99+
return null;
100+
}
101+
}
102+
class Component extends React.Component {
103+
render() {
104+
return <RefComponent ref="refComponent" __self={{}} />;
105+
}
106+
}
107+
108+
ReactNoop.render(<Component />);
109+
expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([
110+
'Warning: Component "Component" contains the string ref "refComponent". ' +
111+
'Support for string refs will be removed in a future major release. ' +
112+
'This case cannot be automatically converted to an arrow function. ' +
113+
'We as you to manually fix this case by using useRef() or createRef() instead. ' +
114+
'Learn more about using refs safely here: ' +
115+
'https://fb.me/react-strict-mode-string-ref',
116+
]);
117+
});
72118
});

packages/react-reconciler/src/ReactChildFiber.js

Lines changed: 11 additions & 1 deletion
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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import invariant from 'shared/invariant';
99
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
1010

1111
import ReactCurrentOwner from './ReactCurrentOwner';
12+
import getComponentName from 'shared/getComponentName';
1213

1314
const hasOwnProperty = Object.prototype.hasOwnProperty;
1415

@@ -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,31 @@ function defineRefPropWarningGetter(props, displayName) {
8996
});
9097
}
9198

99+
function warnIfStringRefCannotBeAutoConverted(config) {
100+
if (
101+
typeof config.ref === 'string' &&
102+
ReactCurrentOwner.current &&
103+
config.__self &&
104+
ReactCurrentOwner.current.stateNode !== config.__self
105+
) {
106+
const componentName = getComponentName(ReactCurrentOwner.current.type);
107+
108+
if (!didWarnAboutStringRefs[componentName]) {
109+
console.error(
110+
'Component "%s" contains the string ref "%s". ' +
111+
'Support for string refs will be removed in a future major release. ' +
112+
'This case cannot be automatically converted to an arrow function. ' +
113+
'We as you to manually fix this case by using useRef() or createRef() instead. ' +
114+
'Learn more about using refs safely here: ' +
115+
'https://fb.me/react-strict-mode-string-ref',
116+
getComponentName(ReactCurrentOwner.current.type),
117+
config.ref,
118+
);
119+
didWarnAboutStringRefs[componentName] = true;
120+
}
121+
}
122+
}
123+
92124
/**
93125
* Factory method to create a new React element. This no longer adheres to
94126
* the class pattern, so do not use new to call it. Also, instanceof check
@@ -260,6 +292,7 @@ export function jsxDEV(type, config, maybeKey, source, self) {
260292

261293
if (hasValidRef(config)) {
262294
ref = config.ref;
295+
warnIfStringRefCannotBeAutoConverted(config);
263296
}
264297

265298
// Remaining properties are added to a new props object
@@ -324,6 +357,9 @@ export function createElement(type, config, children) {
324357
if (config != null) {
325358
if (hasValidRef(config)) {
326359
ref = config.ref;
360+
if (__DEV__) {
361+
warnIfStringRefCannotBeAutoConverted(config);
362+
}
327363
}
328364
if (hasValidKey(config)) {
329365
key = '' + config.key;

0 commit comments

Comments
 (0)