mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Remove string refs (behind flag) (#28322)
Depends on: - https://github.com/facebook/react/pull/28398 --- This removes string refs, which has been deprecated in Strict Mode for seven years. I've left them behind a flag for Meta, but in open source this fully removes the feature.
This commit is contained in:
@@ -38,6 +38,7 @@ describe('ReactComponent', () => {
|
||||
}).toThrowError(/Target container is not a DOM element./);
|
||||
});
|
||||
|
||||
// @gate !disableStringRefs || !__DEV__
|
||||
it('should throw when supplying a string ref outside of render method', async () => {
|
||||
const container = document.createElement('div');
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
@@ -125,6 +126,7 @@ describe('ReactComponent', () => {
|
||||
}
|
||||
});
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('should support string refs on owned components', async () => {
|
||||
const innerObj = {};
|
||||
const outerObj = {};
|
||||
|
||||
@@ -76,6 +76,7 @@ describe('ReactDOMServerIntegration', () => {
|
||||
expect(refElement).toBe(e);
|
||||
});
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('should have string refs on client when rendered over server markup', async () => {
|
||||
class RefsComponent extends React.Component {
|
||||
render() {
|
||||
|
||||
@@ -64,6 +64,7 @@ describe('ReactDeprecationWarnings', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('should warn when given string refs', async () => {
|
||||
class RefComponent extends React.Component {
|
||||
render() {
|
||||
@@ -87,6 +88,7 @@ describe('ReactDeprecationWarnings', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('should warn when owner and self are the same for string refs', async () => {
|
||||
class RefComponent extends React.Component {
|
||||
render() {
|
||||
@@ -109,6 +111,7 @@ describe('ReactDeprecationWarnings', () => {
|
||||
await waitForAll([]);
|
||||
});
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('should warn when owner and self are different for string refs', async () => {
|
||||
class RefComponent extends React.Component {
|
||||
render() {
|
||||
@@ -139,39 +142,39 @@ describe('ReactDeprecationWarnings', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
if (__DEV__) {
|
||||
it('should warn when owner and self are different for string refs', async () => {
|
||||
class RefComponent extends React.Component {
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
// @gate __DEV__
|
||||
// @gate !disableStringRefs
|
||||
it('should warn when owner and self are different for string refs', async () => {
|
||||
class RefComponent extends React.Component {
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
class Component extends React.Component {
|
||||
render() {
|
||||
return JSXDEVRuntime.jsxDEV(
|
||||
RefComponent,
|
||||
{ref: 'refComponent'},
|
||||
null,
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
);
|
||||
}
|
||||
}
|
||||
class Component extends React.Component {
|
||||
render() {
|
||||
return JSXDEVRuntime.jsxDEV(
|
||||
RefComponent,
|
||||
{ref: 'refComponent'},
|
||||
null,
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactNoop.render(<Component />);
|
||||
await expect(async () => await waitForAll([])).toErrorDev([
|
||||
'Warning: Component "Component" contains the string ref "refComponent". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'This case cannot be automatically converted to an arrow function. ' +
|
||||
'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: ' +
|
||||
'https://reactjs.org/link/strict-mode-string-ref',
|
||||
'Warning: Component "Component" contains the string ref "refComponent". ' +
|
||||
'Support for string refs will be removed in a future major release. We recommend ' +
|
||||
'using useRef() or createRef() instead. Learn more about using refs safely here: ' +
|
||||
'https://reactjs.org/link/strict-mode-string-ref',
|
||||
]);
|
||||
});
|
||||
}
|
||||
ReactNoop.render(<Component />);
|
||||
await expect(async () => await waitForAll([])).toErrorDev([
|
||||
'Warning: Component "Component" contains the string ref "refComponent". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'This case cannot be automatically converted to an arrow function. ' +
|
||||
'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: ' +
|
||||
'https://reactjs.org/link/strict-mode-string-ref',
|
||||
'Warning: Component "Component" contains the string ref "refComponent". ' +
|
||||
'Support for string refs will be removed in a future major release. We recommend ' +
|
||||
'using useRef() or createRef() instead. Learn more about using refs safely here: ' +
|
||||
'https://reactjs.org/link/strict-mode-string-ref',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -173,6 +173,7 @@ describe('ReactFunctionComponent', () => {
|
||||
).resolves.not.toThrowError();
|
||||
});
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('should throw on string refs in pure functions', async () => {
|
||||
function Child() {
|
||||
return <div ref="me" />;
|
||||
|
||||
@@ -22,6 +22,7 @@ class TextWithStringRef extends React.Component {
|
||||
}
|
||||
|
||||
describe('when different React version is used with string ref', () => {
|
||||
// @gate !disableStringRefs
|
||||
it('throws the "Refs must have owner" warning', async () => {
|
||||
const container = document.createElement('div');
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
|
||||
@@ -164,6 +164,7 @@ describe('reactiverefs', () => {
|
||||
* Ensure that for every click log there is a corresponding ref (from the
|
||||
* perspective of the injected ClickCounter component.
|
||||
*/
|
||||
// @gate !disableStringRefs
|
||||
it('Should increase refs with an increase in divs', async () => {
|
||||
const testRefsComponent = await renderTestRefsComponent();
|
||||
const clickIncrementer =
|
||||
@@ -366,6 +367,7 @@ describe('ref swapping', () => {
|
||||
expect(refCalled).toBe(1);
|
||||
});
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('coerces numbers to strings', async () => {
|
||||
class A extends React.Component {
|
||||
render() {
|
||||
@@ -390,6 +392,7 @@ describe('ref swapping', () => {
|
||||
expect(a.refs[1].nodeName).toBe('DIV');
|
||||
});
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('provides an error for invalid refs', async () => {
|
||||
const container = document.createElement('div');
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
@@ -547,6 +550,7 @@ describe('creating element with string ref in constructor', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('throws an error', async () => {
|
||||
await expect(async function () {
|
||||
const container = document.createElement('div');
|
||||
@@ -567,6 +571,7 @@ describe('creating element with string ref in constructor', () => {
|
||||
});
|
||||
|
||||
describe('strings refs across renderers', () => {
|
||||
// @gate !disableStringRefs
|
||||
it('does not break', async () => {
|
||||
class Parent extends React.Component {
|
||||
render() {
|
||||
|
||||
+2
-1
@@ -44,7 +44,7 @@ import {
|
||||
import isArray from 'shared/isArray';
|
||||
import assign from 'shared/assign';
|
||||
import {checkPropStringCoercion} from 'shared/CheckStringCoercion';
|
||||
import {enableRefAsProp} from 'shared/ReactFeatureFlags';
|
||||
import {enableRefAsProp, disableStringRefs} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {
|
||||
createWorkInProgress,
|
||||
@@ -266,6 +266,7 @@ function coerceRef(
|
||||
|
||||
let coercedRef;
|
||||
if (
|
||||
!disableStringRefs &&
|
||||
mixedRef !== null &&
|
||||
typeof mixedRef !== 'function' &&
|
||||
typeof mixedRef !== 'object'
|
||||
|
||||
+6
-1
@@ -54,6 +54,7 @@ import {
|
||||
enableFloat,
|
||||
enableLegacyHidden,
|
||||
alwaysThrottleRetries,
|
||||
disableStringRefs,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
FunctionComponent,
|
||||
@@ -1624,7 +1625,11 @@ function commitAttachRef(finishedWork: Fiber) {
|
||||
}
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
if (!ref.hasOwnProperty('current')) {
|
||||
// TODO: We should move these warnings to happen during the render
|
||||
// phase (markRef).
|
||||
if (disableStringRefs && typeof ref === 'string') {
|
||||
console.error('String refs are no longer supported.');
|
||||
} else if (!ref.hasOwnProperty('current')) {
|
||||
console.error(
|
||||
'Unexpected ref object provided for %s. ' +
|
||||
'Use either a ref-setter function or React.createRef().',
|
||||
|
||||
@@ -86,6 +86,7 @@ describe('ReactFiberRefs', () => {
|
||||
});
|
||||
|
||||
// @gate enableRefAsProp
|
||||
// @gate !disableStringRefs
|
||||
test('string ref props are converted to function refs', async () => {
|
||||
let refProp;
|
||||
function Child({ref}) {
|
||||
@@ -112,4 +113,25 @@ describe('ReactFiberRefs', () => {
|
||||
expect(typeof refProp === 'function').toBe(true);
|
||||
expect(owner.refs.child.type).toBe('div');
|
||||
});
|
||||
|
||||
// @gate disableStringRefs
|
||||
test('log an error in dev if a string ref is passed to a ref-receiving component', async () => {
|
||||
let refProp;
|
||||
function Child({ref}) {
|
||||
refProp = ref;
|
||||
return <div ref={ref} />;
|
||||
}
|
||||
|
||||
class Owner extends React.Component {
|
||||
render() {
|
||||
return <Child ref="child" />;
|
||||
}
|
||||
}
|
||||
|
||||
const root = ReactNoop.createRoot();
|
||||
await expect(async () => {
|
||||
await expect(act(() => root.render(<Owner />))).rejects.toThrow();
|
||||
}).toErrorDev('String refs are no longer supported');
|
||||
expect(refProp).toBe('child');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1346,6 +1346,7 @@ describe('ReactIncrementalSideEffects', () => {
|
||||
// TODO: Test that mounts, updates, refs, unmounts and deletions happen in the
|
||||
// expected way for aborted and resumed render life-cycles.
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('supports string refs', async () => {
|
||||
let fooInstance = null;
|
||||
|
||||
|
||||
@@ -535,25 +535,26 @@ describe 'ReactCoffeeScriptClass', ->
|
||||
|
||||
test React.createElement(Foo), 'DIV', 'bar-through-context'
|
||||
|
||||
it 'supports string refs', ->
|
||||
class Foo extends React.Component
|
||||
render: ->
|
||||
React.createElement(InnerComponent,
|
||||
name: 'foo'
|
||||
ref: 'inner'
|
||||
)
|
||||
if !featureFlags.disableStringRefs
|
||||
it 'supports string refs', ->
|
||||
class Foo extends React.Component
|
||||
render: ->
|
||||
React.createElement(InnerComponent,
|
||||
name: 'foo'
|
||||
ref: 'inner'
|
||||
)
|
||||
|
||||
ref = React.createRef()
|
||||
expect(->
|
||||
test(React.createElement(Foo, ref: ref), 'DIV', 'foo')
|
||||
).toErrorDev([
|
||||
'Warning: Component "Foo" contains the string ref "inner". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in Foo (at **)'
|
||||
]);
|
||||
expect(ref.current.refs.inner.getName()).toBe 'foo'
|
||||
ref = React.createRef()
|
||||
expect(->
|
||||
test(React.createElement(Foo, ref: ref), 'DIV', 'foo')
|
||||
).toErrorDev([
|
||||
'Warning: Component "Foo" contains the string ref "inner". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in Foo (at **)'
|
||||
]);
|
||||
expect(ref.current.refs.inner.getName()).toBe 'foo'
|
||||
|
||||
it 'supports drilling through to the DOM using findDOMNode', ->
|
||||
ref = React.createRef()
|
||||
|
||||
@@ -576,24 +576,26 @@ describe('ReactES6Class', () => {
|
||||
});
|
||||
}
|
||||
|
||||
it('supports string refs', () => {
|
||||
class Foo extends React.Component {
|
||||
render() {
|
||||
return <Inner name="foo" ref="inner" />;
|
||||
if (!require('shared/ReactFeatureFlags').disableStringRefs) {
|
||||
it('supports string refs', () => {
|
||||
class Foo extends React.Component {
|
||||
render() {
|
||||
return <Inner name="foo" ref="inner" />;
|
||||
}
|
||||
}
|
||||
}
|
||||
const ref = React.createRef();
|
||||
expect(() => {
|
||||
test(<Foo ref={ref} />, 'DIV', 'foo');
|
||||
}).toErrorDev([
|
||||
'Warning: Component "Foo" contains the string ref "inner". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in Foo (at **)',
|
||||
]);
|
||||
expect(ref.current.refs.inner.getName()).toBe('foo');
|
||||
});
|
||||
const ref = React.createRef();
|
||||
expect(() => {
|
||||
test(<Foo ref={ref} />, 'DIV', 'foo');
|
||||
}).toErrorDev([
|
||||
'Warning: Component "Foo" contains the string ref "inner". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in Foo (at **)',
|
||||
]);
|
||||
expect(ref.current.refs.inner.getName()).toBe('foo');
|
||||
});
|
||||
}
|
||||
|
||||
it('supports drilling through to the DOM using findDOMNode', () => {
|
||||
const ref = React.createRef();
|
||||
|
||||
@@ -118,13 +118,17 @@ describe('ReactProfiler DevTools integration', () => {
|
||||
|
||||
Scheduler.unstable_advanceTime(20);
|
||||
|
||||
function Throws() {
|
||||
throw new Error('Oops!');
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
rendered.update(
|
||||
<div ref="this-will-cause-an-error">
|
||||
<Throws>
|
||||
<AdvanceTime byAmount={3} />
|
||||
</div>,
|
||||
</Throws>,
|
||||
);
|
||||
}).toThrow();
|
||||
}).toThrow('Oops!');
|
||||
|
||||
Scheduler.unstable_advanceTime(20);
|
||||
|
||||
|
||||
@@ -968,6 +968,7 @@ describe('string refs', () => {
|
||||
act = require('internal-test-utils').act;
|
||||
});
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('should warn within a strict tree', async () => {
|
||||
const {StrictMode} = React;
|
||||
|
||||
@@ -1006,6 +1007,7 @@ describe('string refs', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// @gate !disableStringRefs
|
||||
it('should warn within a strict tree', async () => {
|
||||
const {StrictMode} = React;
|
||||
|
||||
|
||||
@@ -689,19 +689,21 @@ describe('ReactTypeScriptClass', function() {
|
||||
});
|
||||
}
|
||||
|
||||
it('supports string refs', function() {
|
||||
const ref = React.createRef();
|
||||
expect(() => {
|
||||
test(React.createElement(ClassicRefs, {ref: ref}), 'DIV', 'foo');
|
||||
}).toErrorDev([
|
||||
'Warning: Component "ClassicRefs" contains the string ref "inner". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in ClassicRefs (at **)',
|
||||
]);
|
||||
expect(ref.current.refs.inner.getName()).toBe('foo');
|
||||
});
|
||||
if (!ReactFeatureFlags.disableStringRefs) {
|
||||
it('supports string refs', function() {
|
||||
const ref = React.createRef();
|
||||
expect(() => {
|
||||
test(React.createElement(ClassicRefs, {ref: ref}), 'DIV', 'foo');
|
||||
}).toErrorDev([
|
||||
'Warning: Component "ClassicRefs" contains the string ref "inner". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in ClassicRefs (at **)',
|
||||
]);
|
||||
expect(ref.current.refs.inner.getName()).toBe('foo');
|
||||
});
|
||||
}
|
||||
|
||||
it('supports drilling through to the DOM using findDOMNode', function() {
|
||||
const ref = React.createRef();
|
||||
|
||||
@@ -18,7 +18,7 @@ import {checkKeyStringCoercion} from 'shared/CheckStringCoercion';
|
||||
import isValidElementType from 'shared/isValidElementType';
|
||||
import isArray from 'shared/isArray';
|
||||
import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame';
|
||||
import {enableRefAsProp} from 'shared/ReactFeatureFlags';
|
||||
import {enableRefAsProp, disableStringRefs} from 'shared/ReactFeatureFlags';
|
||||
|
||||
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||||
const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
|
||||
@@ -62,6 +62,7 @@ function hasValidKey(config) {
|
||||
function warnIfStringRefCannotBeAutoConverted(config, self) {
|
||||
if (__DEV__) {
|
||||
if (
|
||||
!disableStringRefs &&
|
||||
typeof config.ref === 'string' &&
|
||||
ReactCurrentOwner.current &&
|
||||
self &&
|
||||
@@ -536,7 +537,9 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
|
||||
if (!enableRefAsProp) {
|
||||
ref = config.ref;
|
||||
}
|
||||
warnIfStringRefCannotBeAutoConverted(config, self);
|
||||
if (!disableStringRefs) {
|
||||
warnIfStringRefCannotBeAutoConverted(config, self);
|
||||
}
|
||||
}
|
||||
|
||||
// Remaining properties are added to a new props object
|
||||
@@ -665,7 +668,7 @@ export function createElement(type, config, children) {
|
||||
ref = config.ref;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (__DEV__ && !disableStringRefs) {
|
||||
warnIfStringRefCannotBeAutoConverted(config, config.__self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +188,7 @@ export const enableInfiniteRenderLoopDetection = true;
|
||||
// Passes `ref` as a normal prop instead of stripping it from the props object
|
||||
// during element creation.
|
||||
export const enableRefAsProp = __NEXT_MAJOR__;
|
||||
export const disableStringRefs = __NEXT_MAJOR__;
|
||||
|
||||
// Not ready to break experimental yet.
|
||||
// Needs more internal cleanup
|
||||
|
||||
@@ -101,6 +101,7 @@ export const enableInfiniteRenderLoopDetection = false;
|
||||
// TODO: Roll out with GK. Don't keep as dynamic flag for too long, though,
|
||||
// because JSX is an extremely hot path.
|
||||
export const enableRefAsProp = false;
|
||||
export const disableStringRefs = false;
|
||||
|
||||
export const enableReactTestRendererWarning = false;
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ export const enableServerComponentLogs = true;
|
||||
|
||||
// TODO: Should turn this on in next "major" RN release.
|
||||
export const enableRefAsProp = false;
|
||||
export const disableStringRefs = false;
|
||||
|
||||
export const enableReactTestRendererWarning = false;
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ export const enableInfiniteRenderLoopDetection = false;
|
||||
// flags should be handled by the Fiber config.
|
||||
const __NEXT_MAJOR__ = __EXPERIMENTAL__;
|
||||
export const enableRefAsProp = __NEXT_MAJOR__;
|
||||
export const disableStringRefs = __NEXT_MAJOR__;
|
||||
export const enableReactTestRendererWarning = false;
|
||||
export const enableBigIntSupport = __NEXT_MAJOR__;
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ export const enableServerComponentKeys = true;
|
||||
export const enableServerComponentLogs = true;
|
||||
|
||||
export const enableRefAsProp = false;
|
||||
export const disableStringRefs = false;
|
||||
|
||||
export const enableReactTestRendererWarning = false;
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ export const enableServerComponentLogs = true;
|
||||
export const enableInfiniteRenderLoopDetection = false;
|
||||
|
||||
export const enableRefAsProp = false;
|
||||
export const disableStringRefs = false;
|
||||
|
||||
export const enableReactTestRendererWarning = false;
|
||||
|
||||
|
||||
@@ -121,5 +121,9 @@ export const enableReactTestRendererWarning = false;
|
||||
|
||||
export const enableBigIntSupport = false;
|
||||
|
||||
// TODO: Roll out with GK. Don't keep as dynamic flag for too long, though,
|
||||
// because JSX is an extremely hot path.
|
||||
export const disableStringRefs = false;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
|
||||
|
||||
Reference in New Issue
Block a user