[string-refs] cleanup string ref code (#31443)
Jan Kassens committed
Nov 6, 2024 at 14:00 UTC
e1378902bbb322aa1fe1953780f4b2b5f80d26b1
36 files changed
+35
-1160
packages/jest-react/src/JestReact.js
-11
@@ -6,7 +6,6 @@
6
*/
7
8
import {REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
9
-import {disableStringRefs} from 'shared/ReactFeatureFlags';
9
const {assertConsoleLogsCleared} = require('internal-test-utils/consoleMock');
10
11
import isArray from 'shared/isArray';
@@ -56,14 +55,6 @@ function createJSXElementForTestComparison(type, props) {
55
value: null,
56
});
57
return element;
59
- } else if (!__DEV__ && disableStringRefs) {
60
- return {
61
- $$typeof: REACT_ELEMENT_TYPE,
62
- type: type,
63
- key: null,
64
- ref: null,
65
- props: props,
66
- };
58
} else {
59
return {
60
$$typeof: REACT_ELEMENT_TYPE,
@@ -71,8 +62,6 @@ function createJSXElementForTestComparison(type, props) {
62
key: null,
63
ref: null,
64
props: props,
74
- _owner: null,
75
- _store: __DEV__ ? {} : undefined,
65
};
66
}
67
}
packages/react-client/src/ReactFlightClient.js
-14
@@ -41,7 +41,6 @@ import type {Postpone} from 'react/src/ReactPostpone';
41
import type {TemporaryReferenceSet} from './ReactFlightTemporaryReferences';
42
43
import {
44
- disableStringRefs,
44
enableBinaryFlight,
45
enablePostpone,
46
enableFlightReadableStream,
@@ -688,16 +687,6 @@ function createElement(
687
enumerable: false,
688
get: nullRefGetter,
689
});
691
- } else if (!__DEV__ && disableStringRefs) {
692
- element = ({
693
- // This tag allows us to uniquely identify this as a React Element
694
- $$typeof: REACT_ELEMENT_TYPE,
695
-
696
- type,
697
- key,
698
- ref: null,
699
- props,
700
- }: any);
690
} else {
691
element = ({
692
// This tag allows us to uniquely identify this as a React Element
@@ -707,9 +696,6 @@ function createElement(
696
key,
697
ref: null,
698
props,
710
-
711
- // Record the component responsible for creating this element.
712
- _owner: __DEV__ && owner === null ? response._debugRootOwner : owner,
699
}: any);
700
}
701
packages/react-client/src/__tests__/ReactFlight-test.js
+1
-3
@@ -3268,9 +3268,7 @@ describe('ReactFlight', () => {
3268
expect(greeting._owner).toBe(greeting._debugInfo[0]);
3269
} else {
3270
expect(greeting._debugInfo).toBe(undefined);
3271
- expect(greeting._owner).toBe(
3272
- gate(flags => flags.disableStringRefs) ? undefined : null,
3273
- );
3271
+ expect(greeting._owner).toBe(undefined);
3272
}
3273
ReactNoop.render(greeting);
3274
});
packages/react-dom/src/__tests__/ReactComponent-test.js
-112
@@ -42,19 +42,6 @@ describe('ReactComponent', () => {
42
}).toThrowError(/Target container is not a DOM element./);
43
});
44
45
- // @gate !disableStringRefs
46
- it('should throw when supplying a string ref outside of render method', async () => {
47
- const container = document.createElement('div');
48
- const root = ReactDOMClient.createRoot(container);
49
- await expect(
50
- act(() => {
51
- root.render(<div ref="badDiv" />);
52
- }),
53
- // TODO: This throws an AggregateError. Need to update test infra to
54
- // support matching against AggregateError.
55
- ).rejects.toThrow();
56
- });
57
-
45
it('should throw (in dev) when children are mutated during render', async () => {
46
function Wrapper(props) {
47
props.children[1] = <p key={1} />; // Mutation is illegal
@@ -132,105 +119,6 @@ describe('ReactComponent', () => {
119
}
120
});
121
135
- // @gate !disableStringRefs
136
- it('string refs do not detach and reattach on every render', async () => {
137
- let refVal;
138
- class Child extends React.Component {
139
- componentDidUpdate() {
140
- // The parent ref should still be attached because it hasn't changed
141
- // since the last render. If the ref had changed, then this would be
142
- // undefined because refs are attached during the same phase (layout)
143
- // as componentDidUpdate, in child -> parent order. So the new parent
144
- // ref wouldn't have attached yet.
145
- refVal = this.props.contextRef();
146
- }
147
-
148
- render() {
149
- if (this.props.show) {
150
- return <div>child</div>;
151
- }
152
- }
153
- }
154
-
155
- class Parent extends React.Component {
156
- render() {
157
- return (
158
- <div id="test-root" ref="root">
159
- <Child
160
- contextRef={() => this.refs.root}
161
- show={this.props.showChild}
162
- />
163
- </div>
164
- );
165
- }
166
- }
167
-
168
- const container = document.createElement('div');
169
- const root = ReactDOMClient.createRoot(container);
170
-
171
- await act(() => {
172
- root.render(<Parent />);
173
- });
174
-
175
- assertConsoleErrorDev(['contains the string ref']);
176
-
177
- expect(refVal).toBe(undefined);
178
- await act(() => {
179
- root.render(<Parent showChild={true} />);
180
- });
181
- expect(refVal).toBe(container.querySelector('#test-root'));
182
- });
183
-
184
- // @gate !disableStringRefs
185
- it('should support string refs on owned components', async () => {
186
- const innerObj = {};
187
- const outerObj = {};
188
-
189
- class Wrapper extends React.Component {
190
- getObject = () => {
191
- return this.props.object;
192
- };
193
-
194
- render() {
195
- return <div>{this.props.children}</div>;
196
- }
197
- }
198
-
199
- class Component extends React.Component {
200
- render() {
201
- const inner = <Wrapper object={innerObj} ref="inner" />;
202
- const outer = (
203
- <Wrapper object={outerObj} ref="outer">
204
- {inner}
205
- </Wrapper>
206
- );
207
- return outer;
208
- }
209
-
210
- componentDidMount() {
211
- expect(this.refs.inner.getObject()).toEqual(innerObj);
212
- expect(this.refs.outer.getObject()).toEqual(outerObj);
213
- }
214
- }
215
-
216
- const container = document.createElement('div');
217
- const root = ReactDOMClient.createRoot(container);
218
- await expect(async () => {
219
- await act(() => {
220
- root.render(<Component />);
221
- });
222
- }).toErrorDev([
223
- 'Component "Component" contains the string ref "inner". ' +
224
- 'Support for string refs will be removed in a future major release. ' +
225
- 'We recommend using useRef() or createRef() instead. ' +
226
- 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' +
227
- ' in Wrapper (at **)\n' +
228
- ' in div (at **)\n' +
229
- ' in Wrapper (at **)\n' +
230
- ' in Component (at **)',
231
- ]);
232
- });
233
-
122
it('should not have string refs on unmounted components', async () => {
123
class Parent extends React.Component {
124
render() {
packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+2
-5
@@ -537,11 +537,8 @@ describe('ReactCompositeComponent', () => {
537
});
538
539
it('should cleanup even if render() fatals', async () => {
540
- const dispatcherEnabled =
541
- __DEV__ ||
542
- !gate(flags => flags.disableStringRefs) ||
543
- gate(flags => flags.enableCache);
544
- const ownerEnabled = __DEV__ || !gate(flags => flags.disableStringRefs);
540
+ const dispatcherEnabled = __DEV__ || gate(flags => flags.enableCache);
541
+ const ownerEnabled = __DEV__;
542
543
let stashedDispatcher;
544
class BadComponent extends React.Component {
packages/react-dom/src/__tests__/ReactDOMServerIntegrationRefs-test.js
+2
-36
@@ -29,12 +29,8 @@ function initModules() {
29
};
30
}
31
32
-const {
33
- resetModules,
34
- asyncReactDOMRender,
35
- clientRenderOnServerString,
36
- expectMarkupMatch,
37
-} = ReactDOMServerIntegrationUtils(initModules);
32
+const {resetModules, clientRenderOnServerString, expectMarkupMatch} =
33
+ ReactDOMServerIntegrationUtils(initModules);
34
35
describe('ReactDOMServerIntegration', () => {
36
beforeEach(() => {
@@ -75,36 +71,6 @@ describe('ReactDOMServerIntegration', () => {
71
expect(refElement).not.toBe(null);
72
expect(refElement).toBe(e);
73
});
78
-
79
- // @gate !disableStringRefs
80
- it('should have string refs on client when rendered over server markup', async () => {
81
- class RefsComponent extends React.Component {
82
- render() {
83
- return <div ref="myDiv" />;
84
- }
85
- }
86
-
87
- const markup = ReactDOMServer.renderToString(<RefsComponent />);
88
- const root = document.createElement('div');
89
- root.innerHTML = markup;
90
- let component = null;
91
- resetModules();
92
- await expect(async () => {
93
- await asyncReactDOMRender(
94
- <RefsComponent ref={e => (component = e)} />,
95
- root,
96
- true,
97
- );
98
- }).toErrorDev([
99
- 'Component "RefsComponent" contains the string ref "myDiv". ' +
100
- 'Support for string refs will be removed in a future major release. ' +
101
- 'We recommend using useRef() or createRef() instead. ' +
102
- 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' +
103
- ' in div (at **)\n' +
104
- ' in RefsComponent (at **)',
105
- ]);
106
- expect(component.refs.myDiv).toBe(root.firstChild);
107
- });
74
});
75
76
it('should forward refs', async () => {
packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js
-117
@@ -11,7 +11,6 @@
11
12
let React;
13
let ReactNoop;
14
-let JSXDEVRuntime;
14
let waitForAll;
15
16
describe('ReactDeprecationWarnings', () => {
@@ -21,9 +20,6 @@ describe('ReactDeprecationWarnings', () => {
20
ReactNoop = require('react-noop-renderer');
21
const InternalTestUtils = require('internal-test-utils');
22
waitForAll = InternalTestUtils.waitForAll;
24
- if (__DEV__) {
25
- JSXDEVRuntime = require('react/jsx-dev-runtime');
26
- }
23
});
24
25
// @gate !disableDefaultPropsExceptForClasses || !__DEV__
@@ -65,117 +61,4 @@ describe('ReactDeprecationWarnings', () => {
61
'release. Use JavaScript default parameters instead.',
62
);
63
});
68
-
69
- // @gate !disableStringRefs
70
- it('should warn when given string refs', async () => {
71
- class RefComponent extends React.Component {
72
- render() {
73
- return null;
74
- }
75
- }
76
- class Component extends React.Component {
77
- render() {
78
- return <RefComponent ref="refComponent" />;
79
- }
80
- }
81
-
82
- ReactNoop.render(<Component />);
83
- await expect(async () => await waitForAll([])).toErrorDev(
84
- 'Component "Component" contains the string ref "refComponent". ' +
85
- 'Support for string refs will be removed in a future major release. ' +
86
- 'We recommend using useRef() or createRef() instead. ' +
87
- 'Learn more about using refs safely here: ' +
88
- 'https://react.dev/link/strict-mode-string-ref' +
89
- '\n in RefComponent (at **)' +
90
- '\n in Component (at **)',
91
- );
92
- });
93
-
94
- // Disabling this until #28732 lands so we can assert on the warning message.
95
- // (It's already disabled in all but the Meta builds, anyway. Nbd.)
96
- // @gate TODO || !__DEV__
97
- // @gate !disableStringRefs
98
- it('should warn when owner and self are the same for string refs', async () => {
99
- class RefComponent extends React.Component {
100
- render() {
101
- return null;
102
- }
103
- }
104
- class Component extends React.Component {
105
- render() {
106
- return React.createElement(RefComponent, {
107
- ref: 'refComponent',
108
- __self: this,
109
- });
110
- }
111
- }
112
-
113
- ReactNoop.render(<Component />);
114
- await expect(async () => await waitForAll([])).toErrorDev([
115
- 'Component "Component" contains the string ref "refComponent". Support for string refs will be removed in a future major release.',
116
- ]);
117
- await waitForAll([]);
118
- });
119
-
120
- // Disabling this until #28732 lands so we can assert on the warning message.
121
- // (It's already disabled in all but the Meta builds, anyway. Nbd.)
122
- // @gate TODO || !__DEV__
123
- // @gate !disableStringRefs
124
- it('should warn when owner and self are different for string refs (createElement)', async () => {
125
- class RefComponent extends React.Component {
126
- render() {
127
- return null;
128
- }
129
- }
130
- class Component extends React.Component {
131
- render() {
132
- return React.createElement(RefComponent, {
133
- ref: 'refComponent',
134
- __self: {},
135
- });
136
- }
137
- }
138
-
139
- ReactNoop.render(<Component />);
140
- await expect(async () => await waitForAll([])).toErrorDev([
141
- 'Component "Component" contains the string ref "refComponent". ' +
142
- 'Support for string refs will be removed in a future major release. ' +
143
- 'This case cannot be automatically converted to an arrow function. ' +
144
- 'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
145
- 'Learn more about using refs safely here: ' +
146
- 'https://react.dev/link/strict-mode-string-ref',
147
- ]);
148
- });
149
-
150
- // @gate __DEV__
151
- // @gate !disableStringRefs
152
- it('should warn when owner and self are different for string refs (jsx)', async () => {
153
- class RefComponent extends React.Component {
154
- render() {
155
- return null;
156
- }
157
- }
158
- class Component extends React.Component {
159
- render() {
160
- return JSXDEVRuntime.jsxDEV(
161
- RefComponent,
162
- {ref: 'refComponent'},
163
- null,
164
- false,
165
- {},
166
- {},
167
- );
168
- }
169
- }
170
-
171
- ReactNoop.render(<Component />);
172
- await expect(async () => await waitForAll([])).toErrorDev([
173
- 'Component "Component" contains the string ref "refComponent". ' +
174
- 'Support for string refs will be removed in a future major release. ' +
175
- 'This case cannot be automatically converted to an arrow function. ' +
176
- 'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
177
- 'Learn more about using refs safely here: ' +
178
- 'https://react.dev/link/strict-mode-string-ref',
179
- ]);
180
- });
64
});
packages/react-dom/src/__tests__/ReactFunctionComponent-test.js
-18
@@ -179,24 +179,6 @@ describe('ReactFunctionComponent', () => {
179
).resolves.not.toThrowError();
180
});
181
182
- // @gate !disableStringRefs
183
- it('should throw on string refs in pure functions', async () => {
184
- function Child() {
185
- return <div ref="me" />;
186
- }
187
-
188
- const container = document.createElement('div');
189
- const root = ReactDOMClient.createRoot(container);
190
- await expect(
191
- act(() => {
192
- root.render(<Child test="test" />);
193
- }),
194
- )
195
- // TODO: This throws an AggregateError. Need to update test infra to
196
- // support matching against AggregateError.
197
- .rejects.toThrowError();
198
- });
199
-
182
it('should use correct name in key warning', async () => {
183
function Child() {
184
return <div>{[<span />]}</div>;
packages/react-dom/src/__tests__/multiple-copies-of-react-test.js
deleted
-38
@@ -1,38 +0,0 @@
1
-/**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @emails react-core
8
- */
9
-
10
-'use strict';
11
-
12
-let React = require('react');
13
-const ReactDOMClient = require('react-dom/client');
14
-const act = require('internal-test-utils').act;
15
-
16
-class TextWithStringRef extends React.Component {
17
- render() {
18
- jest.resetModules();
19
- React = require('react');
20
- return <span ref="foo">Hello world!</span>;
21
- }
22
-}
23
-
24
-describe('when different React version is used with string ref', () => {
25
- // @gate !disableStringRefs
26
- it('throws the "Refs must have owner" warning', async () => {
27
- const container = document.createElement('div');
28
- const root = ReactDOMClient.createRoot(container);
29
- await expect(
30
- act(() => {
31
- root.render(<TextWithStringRef />);
32
- }),
33
- )
34
- // TODO: This throws an AggregateError. Need to update test infra to
35
- // support matching against AggregateError.
36
- .rejects.toThrow();
37
- });
38
-});
packages/react-dom/src/__tests__/refs-test.js
-301
@@ -13,179 +13,6 @@ const React = require('react');
13
const ReactDOMClient = require('react-dom/client');
14
const act = require('internal-test-utils').act;
15
16
-// This is testing if string refs are deleted from `instance.refs`
17
-// Once support for string refs is removed, this test can be removed.
18
-// Detaching is already tested in refs-detruction-test.js
19
-describe('reactiverefs', () => {
20
- let container;
21
-
22
- afterEach(() => {
23
- if (container) {
24
- document.body.removeChild(container);
25
- container = null;
26
- }
27
- });
28
-
29
- /**
30
- * Counts clicks and has a renders an item for each click. Each item rendered
31
- * has a ref of the form "clickLogN".
32
- */
33
- class ClickCounter extends React.Component {
34
- state = {count: this.props.initialCount};
35
-
36
- triggerReset = () => {
37
- this.setState({count: this.props.initialCount});
38
- };
39
-
40
- handleClick = () => {
41
- this.setState({count: this.state.count + 1});
42
- };
43
-
44
- render() {
45
- const children = [];
46
- let i;
47
- for (i = 0; i < this.state.count; i++) {
48
- children.push(
49
- <div
50
- className="clickLogDiv"
51
- key={'clickLog' + i}
52
- ref={'clickLog' + i}
53
- />,
54
- );
55
- }
56
- return (
57
- <span className="clickIncrementer" onClick={this.handleClick}>
58
- {children}
59
- </span>
60
- );
61
- }
62
- }
63
-
64
- const expectClickLogsLengthToBe = function (instance, length) {
65
- const clickLogs = instance.container.querySelectorAll('.clickLogDiv');
66
- expect(clickLogs.length).toBe(length);
67
- expect(Object.keys(instance.refs.myCounter.refs).length).toBe(length);
68
- };
69
-
70
- /**
71
- * Render a TestRefsComponent and ensure that the main refs are wired up.
72
- */
73
- const renderTestRefsComponent = async function () {
74
- /**
75
- * Only purpose is to test that refs are tracked even when applied to a
76
- * component that is injected down several layers. Ref systems are difficult to
77
- * build in such a way that ownership is maintained in an airtight manner.
78
- */
79
- class GeneralContainerComponent extends React.Component {
80
- render() {
81
- return <div>{this.props.children}</div>;
82
- }
83
- }
84
-
85
- /**
86
- * Notice how refs ownership is maintained even when injecting a component
87
- * into a different parent.
88
- */
89
- class TestRefsComponent extends React.Component {
90
- container = null;
91
- doReset = () => {
92
- this.refs.myCounter.triggerReset();
93
- };
94
-
95
- render() {
96
- return (
97
- <div ref={current => (this.container = current)}>
98
- <div ref="resetDiv" onClick={this.doReset}>
99
- Reset Me By Clicking This.
100
- </div>
101
- <GeneralContainerComponent ref="myContainer">
102
- <ClickCounter ref="myCounter" initialCount={1} />
103
- </GeneralContainerComponent>
104
- </div>
105
- );
106
- }
107
- }
108
-
109
- container = document.createElement('div');
110
- document.body.appendChild(container);
111
-
112
- let testRefsComponent;
113
- await expect(async () => {
114
- const root = ReactDOMClient.createRoot(container);
115
- await act(() => {
116
- root.render(
117
- <TestRefsComponent
118
- ref={current => {
119
- testRefsComponent = current;
120
- }}
121
- />,
122
- );
123
- });
124
- }).toErrorDev([
125
- 'Component "TestRefsComponent" contains the string ' +
126
- 'ref "resetDiv". Support for string refs will be removed in a ' +
127
- 'future major release. We recommend using useRef() or createRef() ' +
128
- 'instead. Learn more about using refs safely ' +
129
- 'here: https://react.dev/link/strict-mode-string-ref\n' +
130
- ' in div (at **)\n' +
131
- ' in div (at **)\n' +
132
- ' in TestRefsComponent (at **)',
133
- 'Component "ClickCounter" contains the string ' +
134
- 'ref "clickLog0". Support for string refs will be removed in a ' +
135
- 'future major release. We recommend using useRef() or createRef() ' +
136
- 'instead. Learn more about using refs safely ' +
137
- 'here: https://react.dev/link/strict-mode-string-ref\n' +
138
- ' in div (at **)\n' +
139
- ' in span (at **)\n' +
140
- ' in ClickCounter (at **)',
141
- ]);
142
-
143
- expect(testRefsComponent instanceof TestRefsComponent).toBe(true);
144
-
145
- const generalContainer = testRefsComponent.refs.myContainer;
146
- expect(generalContainer instanceof GeneralContainerComponent).toBe(true);
147
-
148
- const counter = testRefsComponent.refs.myCounter;
149
- expect(counter instanceof ClickCounter).toBe(true);
150
-
151
- return testRefsComponent;
152
- };
153
-
154
- /**
155
- * Ensure that for every click log there is a corresponding ref (from the
156
- * perspective of the injected ClickCounter component.
157
- */
158
- // @gate !disableStringRefs
159
- it('Should increase refs with an increase in divs', async () => {
160
- const testRefsComponent = await renderTestRefsComponent();
161
- const clickIncrementer =
162
- testRefsComponent.container.querySelector('.clickIncrementer');
163
-
164
- expectClickLogsLengthToBe(testRefsComponent, 1);
165
-
166
- // After clicking the reset, there should still only be one click log ref.
167
- testRefsComponent.refs.resetDiv.click();
168
- expectClickLogsLengthToBe(testRefsComponent, 1);
169
-
170
- // Begin incrementing clicks (and therefore refs).
171
- await act(() => {
172
- clickIncrementer.click();
173
- });
174
- expectClickLogsLengthToBe(testRefsComponent, 2);
175
-
176
- await act(() => {
177
- clickIncrementer.click();
178
- });
179
- expectClickLogsLengthToBe(testRefsComponent, 3);
180
-
181
- // Now reset again
182
- await act(() => {
183
- testRefsComponent.refs.resetDiv.click();
184
- });
185
- expectClickLogsLengthToBe(testRefsComponent, 1);
186
- });
187
-});
188
-
16
/**
17
* Tests that when a ref hops around children, we can track that correctly.
18
*/
@@ -320,32 +147,6 @@ describe('ref swapping', () => {
147
expect(refCalled).toBe(1);
148
});
149
323
- // @gate !disableStringRefs
324
- it('coerces numbers to strings', async () => {
325
- class A extends React.Component {
326
- render() {
327
- return <div ref={1} />;
328
- }
329
- }
330
- let a;
331
- await expect(async () => {
332
- const container = document.createElement('div');
333
- const root = ReactDOMClient.createRoot(container);
334
-
335
- await act(() => {
336
- root.render(<A ref={current => (a = current)} />);
337
- });
338
- }).toErrorDev([
339
- 'Component "A" contains the string ref "1". ' +
340
- 'Support for string refs will be removed in a future major release. ' +
341
- 'We recommend using useRef() or createRef() instead. ' +
342
- 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' +
343
- ' in div (at **)\n' +
344
- ' in A (at **)',
345
- ]);
346
- expect(a.refs[1].nodeName).toBe('DIV');
347
- });
348
-
150
it('provides an error for invalid refs', async () => {
151
const container = document.createElement('div');
152
const root = ReactDOMClient.createRoot(container);
@@ -469,108 +270,6 @@ describe('root level refs', () => {
270
});
271
});
272
472
-describe('creating element with string ref in constructor', () => {
473
- class RefTest extends React.Component {
474
- constructor(props) {
475
- super(props);
476
- this.p = <p ref="p">Hello!</p>;
477
- }
478
-
479
- render() {
480
- return <div>{this.p}</div>;
481
- }
482
- }
483
-
484
- // @gate !disableStringRefs && !__DEV__
485
- it('throws an error in prod', async () => {
486
- await expect(async function () {
487
- const container = document.createElement('div');
488
- const root = ReactDOMClient.createRoot(container);
489
-
490
- await act(() => {
491
- root.render(<RefTest />);
492
- });
493
- })
494
- // TODO: This throws an AggregateError. Need to update test infra to
495
- // support matching against AggregateError.
496
- .rejects.toThrowError();
497
- });
498
-});
499
-
500
-describe('strings refs across renderers', () => {
501
- // @gate !disableStringRefs
502
- it('does not break', async () => {
503
- class Parent extends React.Component {
504
- render() {
505
- // This component owns both refs.
506
- return (
507
- <Indirection
508
- child1={<div ref="child1" />}
509
- child2={<div ref="child2" />}
510
- />
511
- );
512
- }
513
- }
514
-
515
- class Indirection extends React.Component {
516
- componentDidUpdate() {
517
- // One ref is being rendered later using another renderer copy.
518
- jest.resetModules();
519
- const AnotherCopyOfReactDOM = require('react-dom');
520
- const AnotherCopyOfReactDOMClient = require('react-dom/client');
521
- const root = AnotherCopyOfReactDOMClient.createRoot(div2);
522
- AnotherCopyOfReactDOM.flushSync(() => {
523
- root.render(this.props.child2);
524
- });
525
- }
526
- render() {
527
- // The other one is being rendered directly.
528
- return this.props.child1;
529
- }
530
- }
531
-
532
- const div1 = document.createElement('div');
533
- const div2 = document.createElement('div');
534
-
535
- const root = ReactDOMClient.createRoot(div1);
536
- let inst;
537
- await expect(async () => {
538
- await act(() => {
539
- root.render(
540
- <Parent
541
- ref={current => {
542
- if (current !== null) {
543
- inst = current;
544
- }
545
- }}
546
- />,
547
- );
548
- });
549
- }).toErrorDev([
550
- 'Component "Parent" contains the string ref "child1". ' +
551
- 'Support for string refs will be removed in a future major release. ' +
552
- 'We recommend using useRef() or createRef() instead. ' +
553
- 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' +
554
- ' in div (at **)\n' +
555
- ' in Indirection (at **)\n' +
556
- ' in Parent (at **)',
557
- ]);
558
-
559
- // Only the first ref has rendered yet.
560
- expect(inst.refs.child1.tagName).toBe('DIV');
561
- expect(inst.refs.child1).toBe(div1.firstChild);
562
-
563
- // Now both refs should be rendered.
564
- await act(() => {
565
- root.render(<Parent />);
566
- });
567
- expect(inst.refs.child1.tagName).toBe('DIV');
568
- expect(inst.refs.child1).toBe(div1.firstChild);
569
- expect(inst.refs.child2.tagName).toBe('DIV');
570
- expect(inst.refs.child2).toBe(div2.firstChild);
571
- });
572
-});
573
-
273
describe('refs return clean up function', () => {
274
it('calls clean up function if it exists', async () => {
275
const container = document.createElement('div');
packages/react-noop-renderer/src/createReactNoop.js
+1
-11
@@ -35,7 +35,7 @@ import {
35
ConcurrentRoot,
36
LegacyRoot,
37
} from 'react-reconciler/constants';
38
-import {disableLegacyMode, disableStringRefs} from 'shared/ReactFeatureFlags';
38
+import {disableLegacyMode} from 'shared/ReactFeatureFlags';
39
40
import ReactSharedInternals from 'shared/ReactSharedInternals';
41
import ReactVersion from 'shared/ReactVersion';
@@ -843,14 +843,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
843
value: null,
844
});
845
return element;
846
- } else if (!__DEV__ && disableStringRefs) {
847
- return {
848
- $$typeof: REACT_ELEMENT_TYPE,
849
- type: type,
850
- key: null,
851
- ref: null,
852
- props: props,
853
- };
846
} else {
847
return {
848
$$typeof: REACT_ELEMENT_TYPE,
@@ -858,8 +850,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
850
key: null,
851
ref: null,
852
props: props,
861
- _owner: null,
862
- _store: __DEV__ ? {} : undefined,
853
};
854
}
855
}
packages/react-reconciler/src/ReactFiberAsyncDispatcher.js
+1
-3
@@ -14,8 +14,6 @@ import {enableCache} from 'shared/ReactFeatureFlags';
14
import {readContext} from './ReactFiberNewContext';
15
import {CacheContext} from './ReactFiberCacheComponent';
16
17
-import {disableStringRefs} from 'shared/ReactFeatureFlags';
18
-
17
import {current as currentOwner} from './ReactCurrentFiber';
18
19
function getCacheForType<T>(resourceType: () => T): T {
@@ -35,7 +33,7 @@ export const DefaultAsyncDispatcher: AsyncDispatcher = ({
33
getCacheForType,
34
}: any);
35
38
-if (__DEV__ || !disableStringRefs) {
36
+if (__DEV__) {
37
DefaultAsyncDispatcher.getOwner = (): null | Fiber => {
38
return currentOwner;
39
};
packages/react-reconciler/src/ReactFiberBeginWork.js
+1
-21
@@ -110,7 +110,6 @@ import {
110
enableRenderableContext,
111
disableLegacyMode,
112
disableDefaultPropsExceptForClasses,
113
- disableStringRefs,
113
enableOwnerStacks,
114
} from 'shared/ReactFeatureFlags';
115
import isArray from 'shared/isArray';
@@ -1052,25 +1051,6 @@ function markRef(current: Fiber | null, workInProgress: Fiber) {
1051
);
1052
}
1053
if (current === null || current.ref !== ref) {
1055
- if (!disableStringRefs && current !== null) {
1056
- const oldRef = current.ref;
1057
- const newRef = ref;
1058
- if (
1059
- typeof oldRef === 'function' &&
1060
- typeof newRef === 'function' &&
1061
- typeof oldRef.__stringRef === 'string' &&
1062
- oldRef.__stringRef === newRef.__stringRef &&
1063
- oldRef.__stringRefType === newRef.__stringRefType &&
1064
- oldRef.__stringRefOwner === newRef.__stringRefOwner
1065
- ) {
1066
- // Although this is a different callback, it represents the same
1067
- // string ref. To avoid breaking old Meta code that relies on string
1068
- // refs only being attached once, reuse the old ref. This will
1069
- // prevent us from detaching and reattaching the ref on each update.
1070
- workInProgress.ref = oldRef;
1071
- return;
1072
- }
1073
- }
1054
// Schedule a Ref effect
1055
workInProgress.flags |= Ref | RefStatic;
1056
}
@@ -1388,7 +1368,7 @@ function finishClassComponent(
1368
const instance = workInProgress.stateNode;
1369
1370
// Rerender
1391
- if (__DEV__ || !disableStringRefs) {
1371
+ if (__DEV__) {
1372
setCurrentFiber(workInProgress);
1373
}
1374
let nextChildren;
packages/react-reconciler/src/ReactFiberCommitEffects.js
+1
-2
@@ -18,7 +18,6 @@ import {
18
enableProfilerNestedUpdatePhase,
19
enableSchedulingProfiler,
20
enableScopeAPI,
21
- disableStringRefs,
21
} from 'shared/ReactFeatureFlags';
22
import {
23
ClassComponent,
@@ -773,7 +772,7 @@ function commitAttachRef(finishedWork: Fiber) {
772
if (__DEV__) {
773
// TODO: We should move these warnings to happen during the render
774
// phase (markRef).
776
- if (disableStringRefs && typeof ref === 'string') {
775
+ if (typeof ref === 'string') {
776
console.error('String refs are no longer supported.');
777
} else if (!ref.hasOwnProperty('current')) {
778
console.error(
packages/react-reconciler/src/ReactFiberWorkLoop.js
+3
-12
@@ -40,7 +40,6 @@ import {
40
enableInfiniteRenderLoopDetection,
41
disableLegacyMode,
42
disableDefaultPropsExceptForClasses,
43
- disableStringRefs,
43
enableSiblingPrerendering,
44
enableComponentPerformanceTrack,
45
} from 'shared/ReactFeatureFlags';
@@ -1732,7 +1731,7 @@ function handleThrow(root: FiberRoot, thrownValue: any): void {
1731
// These should be reset immediately because they're only supposed to be set
1732
// when React is executing user code.
1733
resetHooksAfterThrow();
1735
- if (__DEV__ || !disableStringRefs) {
1734
+ if (__DEV__) {
1735
resetCurrentFiber();
1736
}
1737
@@ -1928,7 +1927,7 @@ function popDispatcher(prevDispatcher: any) {
1927
}
1928
1929
function pushAsyncDispatcher() {
1931
- if (enableCache || __DEV__ || !disableStringRefs) {
1930
+ if (enableCache || __DEV__) {
1931
const prevAsyncDispatcher = ReactSharedInternals.A;
1932
ReactSharedInternals.A = DefaultAsyncDispatcher;
1933
return prevAsyncDispatcher;
@@ -1938,7 +1937,7 @@ function pushAsyncDispatcher() {
1937
}
1938
1939
function popAsyncDispatcher(prevAsyncDispatcher: any) {
1941
- if (enableCache || __DEV__ || !disableStringRefs) {
1940
+ if (enableCache || __DEV__) {
1941
ReactSharedInternals.A = prevAsyncDispatcher;
1942
}
1943
}
@@ -2497,9 +2496,6 @@ function performUnitOfWork(unitOfWork: Fiber): void {
2496
}
2497
}
2498
2500
- if (!disableStringRefs) {
2501
- resetCurrentFiber();
2502
- }
2499
unitOfWork.memoizedProps = unitOfWork.pendingProps;
2500
if (next === null) {
2501
// If this doesn't spawn new work, complete the current work.
@@ -2519,11 +2515,6 @@ function replaySuspendedUnitOfWork(unitOfWork: Fiber): void {
2515
next = replayBeginWork(unitOfWork);
2516
}
2517
2522
- // The begin phase finished successfully without suspending. Return to the
2523
- // normal work loop.
2524
- if (!disableStringRefs) {
2525
- resetCurrentFiber();
2526
- }
2518
unitOfWork.memoizedProps = unitOfWork.pendingProps;
2519
if (next === null) {
2520
// If this doesn't spawn new work, complete the current work.
packages/react-reconciler/src/ReactInternalTypes.js
+1
-1
@@ -460,6 +460,6 @@ export type Dispatcher = {
460
461
export type AsyncDispatcher = {
462
getCacheForType: <T>(resourceType: () => T) => T,
463
- // DEV-only (or !disableStringRefs)
463
+ // DEV-only
464
getOwner: () => null | Fiber | ReactComponentInfo | ComponentStackNode,
465
};
packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js
-29
@@ -85,35 +85,6 @@ describe('ReactFiberRefs', () => {
85
expect(ref2.current).not.toBe(null);
86
});
87
88
- // @gate !disableStringRefs
89
- it('string ref props are converted to function refs', async () => {
90
- let refProp;
91
- function Child({ref}) {
92
- refProp = ref;
93
- return <div ref={ref} />;
94
- }
95
-
96
- let owner;
97
- class Owner extends React.Component {
98
- render() {
99
- owner = this;
100
- return <Child ref="child" />;
101
- }
102
- }
103
-
104
- const root = ReactNoop.createRoot();
105
- await act(() => root.render(<Owner />));
106
-
107
- // When string refs aren't disabled, string refs
108
- // the receiving component receives a callback ref, not the original string.
109
- // This behavior should never be shipped to open source; it's only here to
110
- // allow Meta to keep using string refs temporarily while they finish
111
- // migrating their codebase.
112
- expect(typeof refProp === 'function').toBe(true);
113
- expect(owner.refs.child.type).toBe('div');
114
- });
115
-
116
- // @gate disableStringRefs
88
it('throw if a string ref is passed to a ref-receiving component', async () => {
89
let refProp;
90
function Child({ref}) {
packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
-34
@@ -1334,38 +1334,4 @@ describe('ReactIncrementalSideEffects', () => {
1334
1335
// TODO: Test that mounts, updates, refs, unmounts and deletions happen in the
1336
// expected way for aborted and resumed render life-cycles.
1337
-
1338
- // @gate !disableStringRefs
1339
- it('supports string refs', async () => {
1340
- let fooInstance = null;
1341
-
1342
- class Bar extends React.Component {
1343
- componentDidMount() {
1344
- this.test = 'test';
1345
- }
1346
- render() {
1347
- return <div />;
1348
- }
1349
- }
1350
-
1351
- class Foo extends React.Component {
1352
- render() {
1353
- fooInstance = this;
1354
- return <Bar ref="bar" />;
1355
- }
1356
- }
1357
-
1358
- ReactNoop.render(<Foo />);
1359
- await expect(async () => {
1360
- await waitForAll([]);
1361
- }).toErrorDev([
1362
- 'Component "Foo" contains the string ref "bar". ' +
1363
- 'Support for string refs will be removed in a future major release. ' +
1364
- 'We recommend using useRef() or createRef() instead. ' +
1365
- 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' +
1366
- ' in Bar (at **)\n' +
1367
- ' in Foo (at **)',
1368
- ]);
1369
- expect(fooInstance.refs.bar.test).toEqual('test');
1370
- });
1337
});
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js
+1
-3
@@ -1001,9 +1001,7 @@ describe('ReactFlightDOMEdge', () => {
1001
expect(greeting._owner).toBe(lazyWrapper._debugInfo[0]);
1002
} else {
1003
expect(lazyWrapper._debugInfo).toBe(undefined);
1004
- expect(greeting._owner).toBe(
1005
- gate(flags => flags.disableStringRefs) ? undefined : null,
1006
- );
1004
+ expect(greeting._owner).toBe(undefined);
1005
}
1006
});
1007
packages/react-server/src/ReactFizzAsyncDispatcher.js
-6
@@ -10,8 +10,6 @@
10
import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
11
import type {ComponentStackNode} from './ReactFizzComponentStack';
12
13
-import {disableStringRefs} from 'shared/ReactFeatureFlags';
14
-
13
import {currentTaskInDEV} from './ReactFizzCurrentTask';
14
15
function getCacheForType<T>(resourceType: () => T): T {
@@ -29,8 +27,4 @@ if (__DEV__) {
27
}
28
return currentTaskInDEV.componentStack;
29
};
32
-} else if (!disableStringRefs) {
33
- DefaultAsyncDispatcher.getOwner = (): null => {
34
- return null;
35
- };
30
}
packages/react-server/src/ReactFizzServer.js
+1
-2
@@ -162,7 +162,6 @@ import {
162
enableRenderableContext,
163
disableDefaultPropsExceptForClasses,
164
enableAsyncIterableChildren,
165
- disableStringRefs,
165
enableOwnerStacks,
166
} from 'shared/ReactFeatureFlags';
167
@@ -4452,7 +4451,7 @@ export function performWork(request: Request): void {
4451
const prevDispatcher = ReactSharedInternals.H;
4452
ReactSharedInternals.H = HooksDispatcher;
4453
let prevAsyncDispatcher = null;
4455
- if (enableCache || __DEV__ || !disableStringRefs) {
4454
+ if (enableCache || __DEV__) {
4455
prevAsyncDispatcher = ReactSharedInternals.A;
4456
ReactSharedInternals.A = DefaultAsyncDispatcher;
4457
}
packages/react-server/src/flight/ReactFlightAsyncDispatcher.js
-10
@@ -7,14 +7,9 @@
7
* @flow
8
*/
9
10
-import type {ReactComponentInfo} from 'shared/ReactTypes';
11
-
10
import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
11
12
import {resolveRequest, getCache} from '../ReactFlightServer';
15
-
16
-import {disableStringRefs} from 'shared/ReactFeatureFlags';
17
-
13
import {resolveOwner} from './ReactFlightCurrentOwner';
14
15
function resolveCache(): Map<Function, mixed> {
@@ -40,9 +35,4 @@ export const DefaultAsyncDispatcher: AsyncDispatcher = ({
35
36
if (__DEV__) {
37
DefaultAsyncDispatcher.getOwner = resolveOwner;
43
-} else if (!disableStringRefs) {
44
- // Server Components never use string refs but the JSX runtime looks for it.
45
- DefaultAsyncDispatcher.getOwner = (): null | ReactComponentInfo => {
46
- return null;
47
- };
38
}
packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee
-21
@@ -551,25 +551,4 @@ describe 'ReactCoffeeScriptClass', ->
551
],
552
)
553
554
- if !featureFlags.disableStringRefs
555
- it 'supports string refs', ->
556
- class Foo extends React.Component
557
- render: ->
558
- React.createElement(InnerComponent,
559
- name: 'foo'
560
- ref: 'inner'
561
- )
562
-
563
- ref = React.createRef()
564
- expect(->
565
- test(React.createElement(Foo, ref: ref), 'DIV', 'foo')
566
- ).toErrorDev([
567
- 'Component "Foo" contains the string ref "inner". ' +
568
- 'Support for string refs will be removed in a future major release. ' +
569
- 'We recommend using useRef() or createRef() instead. ' +
570
- 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' +
571
- ' in _Class (at **)'
572
- ]);
573
- expect(ref.current.refs.inner.getName()).toBe 'foo'
574
-
554
undefined
packages/react/src/__tests__/ReactCreateElement-test.js
+1
-1
@@ -218,7 +218,7 @@ describe('ReactCreateElement', () => {
218
}
219
const root = ReactDOMClient.createRoot(document.createElement('div'));
220
await act(() => root.render(React.createElement(Wrapper)));
221
- if (__DEV__ || !gate(flags => flags.disableStringRefs)) {
221
+ if (__DEV__) {
222
expect(element._owner.stateNode).toBe(instance);
223
} else {
224
expect('_owner' in element).toBe(false);
packages/react/src/__tests__/ReactES6Class-test.js
-21
@@ -592,25 +592,4 @@ describe('ReactES6Class', () => {
592
]);
593
});
594
}
595
-
596
- if (!require('shared/ReactFeatureFlags').disableStringRefs) {
597
- it('supports string refs', () => {
598
- class Foo extends React.Component {
599
- render() {
600
- return <Inner name="foo" ref="inner" />;
601
- }
602
- }
603
- const ref = React.createRef();
604
- expect(() => {
605
- runTest(<Foo ref={ref} />, 'DIV', 'foo');
606
- }).toErrorDev([
607
- 'Component "Foo" contains the string ref "inner". ' +
608
- 'Support for string refs will be removed in a future major release. ' +
609
- 'We recommend using useRef() or createRef() instead. ' +
610
- 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' +
611
- ' in Inner (at **)',
612
- ]);
613
- expect(ref.current.refs.inner.getName()).toBe('foo');
614
- });
615
- }
595
});
packages/react/src/__tests__/ReactElementClone-test.js
+8
-60
@@ -270,49 +270,8 @@ describe('ReactElementClone', () => {
270
271
const root = ReactDOMClient.createRoot(document.createElement('div'));
272
await act(() => root.render(<Grandparent />));
273
- if (gate(flags => flags.disableStringRefs)) {
274
- expect(component.childRef).toEqual({current: null});
275
- expect(component.parentRef.current.xyzRef.current.tagName).toBe('SPAN');
276
- } else if (gate(flags => !flags.disableStringRefs)) {
277
- expect(component.childRef).toEqual({current: null});
278
- expect(component.parentRef.current.xyzRef.current.tagName).toBe('SPAN');
279
- } else {
280
- // Not going to bother testing every possible combination.
281
- }
282
- });
283
-
284
- // @gate !disableStringRefs
285
- it('should steal the ref if a new string ref is specified without an owner', async () => {
286
- // Regression test for this specific feature combination calling cloneElement on an element
287
- // without an owner
288
- await expect(async () => {
289
- // create an element without an owner
290
- const element = React.createElement('div', {id: 'some-id'});
291
- class Parent extends React.Component {
292
- render() {
293
- return <Child>{element}</Child>;
294
- }
295
- }
296
- let child;
297
- class Child extends React.Component {
298
- render() {
299
- child = this;
300
- const clone = React.cloneElement(this.props.children, {
301
- ref: 'xyz',
302
- });
303
- return <div>{clone}</div>;
304
- }
305
- }
306
-
307
- const root = ReactDOMClient.createRoot(document.createElement('div'));
308
- await act(() => root.render(<Parent />));
309
- expect(child.refs.xyz.tagName).toBe('DIV');
310
- }).toErrorDev([
311
- 'Component "Child" contains the string ref "xyz". Support for ' +
312
- 'string refs will be removed in a future major release. We recommend ' +
313
- 'using useRef() or createRef() instead. Learn more about using refs ' +
314
- 'safely here: https://react.dev/link/strict-mode-string-ref',
315
- ]);
273
+ expect(component.childRef).toEqual({current: null});
274
+ expect(component.parentRef.current.xyzRef.current.tagName).toBe('SPAN');
275
});
276
277
it('should overwrite props', async () => {
@@ -403,23 +362,12 @@ describe('ReactElementClone', () => {
362
const clone = React.cloneElement(element, props);
363
expect(clone.type).toBe(ComponentClass);
364
expect(clone.key).toBe('12');
406
- if (gate(flags => flags.disableStringRefs)) {
407
- expect(clone.props.ref).toBe('34');
408
- expect(() => expect(clone.ref).toBe('34')).toErrorDev(
409
- 'Accessing element.ref was removed in React 19',
410
- {withoutStack: true},
411
- );
412
- expect(clone.props).toEqual({foo: 'ef', ref: '34'});
413
- } else if (gate(flags => !flags.disableStringRefs)) {
414
- expect(() => {
415
- expect(clone.ref).toBe(element.ref);
416
- }).toErrorDev('Accessing element.ref was removed in React 19', {
417
- withoutStack: true,
418
- });
419
- expect(clone.props).toEqual({foo: 'ef', ref: element.ref});
420
- } else {
421
- // Not going to bother testing every possible combination.
422
- }
365
+ expect(clone.props.ref).toBe('34');
366
+ expect(() => expect(clone.ref).toBe('34')).toErrorDev(
367
+ 'Accessing element.ref was removed in React 19',
368
+ {withoutStack: true},
369
+ );
370
+ expect(clone.props).toEqual({foo: 'ef', ref: '34'});
371
if (__DEV__) {
372
expect(Object.isFrozen(element)).toBe(true);
373
expect(Object.isFrozen(element.props)).toBe(true);
packages/react/src/__tests__/ReactStrictMode-test.js
-49
@@ -956,55 +956,6 @@ describe('symbol checks', () => {
956
});
957
});
958
959
-describe('string refs', () => {
960
- beforeEach(() => {
961
- jest.resetModules();
962
- React = require('react');
963
- ReactDOM = require('react-dom');
964
- ReactDOMClient = require('react-dom/client');
965
- act = require('internal-test-utils').act;
966
- });
967
-
968
- // @gate !disableStringRefs
969
- it('should warn within a strict tree', async () => {
970
- const {StrictMode} = React;
971
-
972
- class OuterComponent extends React.Component {
973
- render() {
974
- return (
975
- <StrictMode>
976
- <InnerComponent ref="somestring" />
977
- </StrictMode>
978
- );
979
- }
980
- }
981
-
982
- class InnerComponent extends React.Component {
983
- render() {
984
- return null;
985
- }
986
- }
987
-
988
- const container = document.createElement('div');
989
- const root = ReactDOMClient.createRoot(container);
990
- await expect(async () => {
991
- await act(() => {
992
- root.render(<OuterComponent />);
993
- });
994
- }).toErrorDev(
995
- 'Component "OuterComponent" contains the string ref "somestring". ' +
996
- 'Support for string refs will be removed in a future major release. ' +
997
- 'We recommend using useRef() or createRef() instead. ' +
998
- 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' +
999
- ' in InnerComponent (at **)',
1000
- );
1001
-
1002
- await act(() => {
1003
- root.render(<OuterComponent />);
1004
- });
1005
- });
1006
-});
1007
-
959
describe('context legacy', () => {
960
beforeEach(() => {
961
jest.resetModules();
packages/react/src/__tests__/ReactTypeScriptClass-test.ts
-16
@@ -697,20 +697,4 @@ describe('ReactTypeScriptClass', function() {
697
] );
698
});
699
}
700
-
701
- if (!ReactFeatureFlags.disableStringRefs) {
702
- it('supports string refs', function() {
703
- const ref = React.createRef();
704
- expect(() => {
705
- test(React.createElement(ClassicRefs, {ref: ref}), 'DIV', 'foo');
706
- }).toErrorDev([
707
- 'Component "ClassicRefs" contains the string ref "inner". ' +
708
- 'Support for string refs will be removed in a future major release. ' +
709
- 'We recommend using useRef() or createRef() instead. ' +
710
- 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' +
711
- ' in Inner (at **)',
712
- ]);
713
- expect(ref.current.refs.inner.getName()).toBe('foo');
714
- });
715
- }
700
});
packages/react/src/jsx/ReactJSXElement.js
+11
-194
@@ -20,13 +20,9 @@ import isValidElementType from 'shared/isValidElementType';
20
import isArray from 'shared/isArray';
21
import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame';
22
import {
23
- disableStringRefs,
23
disableDefaultPropsExceptForClasses,
24
enableOwnerStacks,
25
} from 'shared/ReactFeatureFlags';
27
-import {checkPropStringCoercion} from 'shared/CheckStringCoercion';
28
-import {ClassComponent} from 'react-reconciler/src/ReactWorkTags';
29
-import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
26
27
const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference');
28
@@ -59,7 +55,7 @@ function getTaskName(type) {
55
}
56
57
function getOwner() {
62
- if (__DEV__ || !disableStringRefs) {
58
+ if (__DEV__) {
59
const dispatcher = ReactSharedInternals.A;
60
if (dispatcher === null) {
61
return null;
@@ -70,17 +66,13 @@ function getOwner() {
66
}
67
68
let specialPropKeyWarningShown;
73
-let didWarnAboutStringRefs;
69
let didWarnAboutElementRef;
70
let didWarnAboutOldJSXRuntime;
71
72
if (__DEV__) {
78
- didWarnAboutStringRefs = {};
73
didWarnAboutElementRef = {};
74
}
75
82
-const enableFastJSXWithoutStringRefs = disableStringRefs;
83
-
76
function hasValidRef(config) {
77
if (__DEV__) {
78
if (hasOwnProperty.call(config, 'ref')) {
@@ -105,35 +97,6 @@ function hasValidKey(config) {
97
return config.key !== undefined;
98
}
99
108
-function warnIfStringRefCannotBeAutoConverted(config, self) {
109
- if (__DEV__) {
110
- let owner;
111
- if (
112
- !disableStringRefs &&
113
- typeof config.ref === 'string' &&
114
- (owner = getOwner()) &&
115
- self &&
116
- owner.stateNode !== self
117
- ) {
118
- const componentName = getComponentNameFromType(owner.type);
119
-
120
- if (!didWarnAboutStringRefs[componentName]) {
121
- console.error(
122
- 'Component "%s" contains the string ref "%s". ' +
123
- 'Support for string refs will be removed in a future major release. ' +
124
- 'This case cannot be automatically converted to an arrow function. ' +
125
- 'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
126
- 'Learn more about using refs safely here: ' +
127
- 'https://react.dev/link/strict-mode-string-ref',
128
- getComponentNameFromType(owner.type),
129
- config.ref,
130
- );
131
- didWarnAboutStringRefs[componentName] = true;
132
- }
133
- }
134
- }
135
-}
136
-
100
function defineKeyPropWarningGetter(props, displayName) {
101
if (__DEV__) {
102
const warnAboutAccessingKey = function () {
@@ -259,22 +222,8 @@ function ReactElement(
222
value: null,
223
});
224
}
262
- } else if (!__DEV__ && disableStringRefs) {
263
- // In prod, `ref` is a regular property and _owner doesn't exist.
264
- element = {
265
- // This tag allows us to uniquely identify this as a React Element
266
- $$typeof: REACT_ELEMENT_TYPE,
267
-
268
- // Built-in properties that belong on the element
269
- type,
270
- key,
271
- ref,
272
-
273
- props,
274
- };
225
} else {
276
- // In prod, `ref` is a regular property. It will be removed in a
277
- // future release.
226
+ // In prod, `ref` is a regular property and _owner doesn't exist.
227
element = {
228
// This tag allows us to uniquely identify this as a React Element
229
$$typeof: REACT_ELEMENT_TYPE,
@@ -285,9 +234,6 @@ function ReactElement(
234
ref,
235
236
props,
288
-
289
- // Record the component responsible for creating this element.
290
- _owner: owner,
237
};
238
}
239
@@ -368,10 +314,7 @@ export function jsxProd(type, config, maybeKey) {
314
}
315
316
let props;
371
- if (
372
- (enableFastJSXWithoutStringRefs || !('ref' in config)) &&
373
- !('key' in config)
374
- ) {
317
+ if (!('key' in config)) {
318
// If key was not spread in, we can reuse the original props object. This
319
// only works for `jsx`, not `createElement`, because `jsx` is a compiler
320
// target and the compiler always passes a new object. For `createElement`,
@@ -390,11 +333,7 @@ export function jsxProd(type, config, maybeKey) {
333
for (const propName in config) {
334
// Skip over reserved prop names
335
if (propName !== 'key') {
393
- if (!disableStringRefs && propName === 'ref') {
394
- props.ref = coerceStringRef(config[propName], getOwner(), type);
395
- } else {
396
- props[propName] = config[propName];
397
- }
336
+ props[propName] = config[propName];
337
}
338
}
339
}
@@ -637,17 +576,8 @@ function jsxDEVImpl(
576
key = '' + config.key;
577
}
578
640
- if (!disableStringRefs) {
641
- if (hasValidRef(config)) {
642
- warnIfStringRefCannotBeAutoConverted(config, self);
643
- }
644
- }
645
-
579
let props;
647
- if (
648
- (enableFastJSXWithoutStringRefs || !('ref' in config)) &&
649
- !('key' in config)
650
- ) {
580
+ if (!('key' in config)) {
581
// If key was not spread in, we can reuse the original props object. This
582
// only works for `jsx`, not `createElement`, because `jsx` is a compiler
583
// target and the compiler always passes a new object. For `createElement`,
@@ -666,11 +596,7 @@ function jsxDEVImpl(
596
for (const propName in config) {
597
// Skip over reserved prop names
598
if (propName !== 'key') {
669
- if (!disableStringRefs && propName === 'ref') {
670
- props.ref = coerceStringRef(config[propName], getOwner(), type);
671
- } else {
672
- props[propName] = config[propName];
673
- }
599
+ props[propName] = config[propName];
600
}
601
}
602
}
@@ -800,11 +726,6 @@ export function createElement(type, config, children) {
726
}
727
}
728
803
- if (__DEV__ && !disableStringRefs) {
804
- if (hasValidRef(config)) {
805
- warnIfStringRefCannotBeAutoConverted(config, config.__self);
806
- }
807
- }
729
if (hasValidKey(config)) {
730
if (__DEV__) {
731
checkKeyStringCoercion(config.key);
@@ -825,11 +746,7 @@ export function createElement(type, config, children) {
746
propName !== '__self' &&
747
propName !== '__source'
748
) {
828
- if (!disableStringRefs && propName === 'ref') {
829
- props.ref = coerceStringRef(config[propName], getOwner(), type);
830
- } else {
831
- props[propName] = config[propName];
832
- }
749
+ props[propName] = config[propName];
750
}
751
}
752
}
@@ -889,7 +806,7 @@ export function cloneAndReplaceKey(oldElement, newKey) {
806
newKey,
807
undefined,
808
undefined,
892
- !__DEV__ && disableStringRefs ? undefined : oldElement._owner,
809
+ !__DEV__ ? undefined : oldElement._owner,
810
oldElement.props,
811
__DEV__ && enableOwnerStacks ? oldElement._debugStack : undefined,
812
__DEV__ && enableOwnerStacks ? oldElement._debugTask : undefined,
@@ -921,11 +838,11 @@ export function cloneElement(element, config, children) {
838
let key = element.key;
839
840
// Owner will be preserved, unless ref is overridden
924
- let owner = !__DEV__ && disableStringRefs ? undefined : element._owner;
841
+ let owner = !__DEV__ ? undefined : element._owner;
842
843
if (config != null) {
844
if (hasValidRef(config)) {
928
- owner = __DEV__ || !disableStringRefs ? getOwner() : undefined;
845
+ owner = __DEV__ ? getOwner() : undefined;
846
}
847
if (hasValidKey(config)) {
848
if (__DEV__) {
@@ -969,11 +886,7 @@ export function cloneElement(element, config, children) {
886
// Resolve default props
887
props[propName] = defaultProps[propName];
888
} else {
972
- if (!disableStringRefs && propName === 'ref') {
973
- props.ref = coerceStringRef(config[propName], owner, element.type);
974
- } else {
975
- props[propName] = config[propName];
976
- }
889
+ props[propName] = config[propName];
890
}
891
}
892
}
@@ -1173,99 +1086,3 @@ function getCurrentComponentErrorInfo(parentType) {
1086
return info;
1087
}
1088
}
1176
-
1177
-function coerceStringRef(mixedRef, owner, type) {
1178
- if (disableStringRefs) {
1179
- return mixedRef;
1180
- }
1181
-
1182
- let stringRef;
1183
- if (typeof mixedRef === 'string') {
1184
- stringRef = mixedRef;
1185
- } else {
1186
- if (typeof mixedRef === 'number' || typeof mixedRef === 'boolean') {
1187
- if (__DEV__) {
1188
- checkPropStringCoercion(mixedRef, 'ref');
1189
- }
1190
- stringRef = '' + mixedRef;
1191
- } else {
1192
- return mixedRef;
1193
- }
1194
- }
1195
-
1196
- const callback = stringRefAsCallbackRef.bind(null, stringRef, type, owner);
1197
- // This is used to check whether two callback refs conceptually represent
1198
- // the same string ref, and can therefore be reused by the reconciler. Needed
1199
- // for backwards compatibility with old Meta code that relies on string refs
1200
- // not being reattached on every render.
1201
- callback.__stringRef = stringRef;
1202
- callback.__type = type;
1203
- callback.__owner = owner;
1204
- return callback;
1205
-}
1206
-
1207
-function stringRefAsCallbackRef(stringRef, type, owner, value) {
1208
- if (disableStringRefs) {
1209
- return;
1210
- }
1211
- if (!owner) {
1212
- throw new Error(
1213
- `Element ref was specified as a string (${stringRef}) but no owner was set. This could happen for one of` +
1214
- ' the following reasons:\n' +
1215
- '1. You may be adding a ref to a function component\n' +
1216
- "2. You may be adding a ref to a component that was not created inside a component's render method\n" +
1217
- '3. You have multiple copies of React loaded\n' +
1218
- 'See https://react.dev/link/refs-must-have-owner for more information.',
1219
- );
1220
- }
1221
- if (owner.tag !== ClassComponent) {
1222
- throw new Error(
1223
- 'Function components cannot have string refs. ' +
1224
- 'We recommend using useRef() instead. ' +
1225
- 'Learn more about using refs safely here: ' +
1226
- 'https://react.dev/link/strict-mode-string-ref',
1227
- );
1228
- }
1229
-
1230
- if (__DEV__) {
1231
- if (
1232
- // Will already warn with "Function components cannot be given refs"
1233
- !(typeof type === 'function' && !isReactClass(type))
1234
- ) {
1235
- const componentName = getComponentNameFromFiber(owner) || 'Component';
1236
- if (!didWarnAboutStringRefs[componentName]) {
1237
- if (__DEV__) {
1238
- console.error(
1239
- 'Component "%s" contains the string ref "%s". Support for string refs ' +
1240
- 'will be removed in a future major release. We recommend using ' +
1241
- 'useRef() or createRef() instead. ' +
1242
- 'Learn more about using refs safely here: ' +
1243
- 'https://react.dev/link/strict-mode-string-ref',
1244
- componentName,
1245
- stringRef,
1246
- );
1247
- }
1248
- didWarnAboutStringRefs[componentName] = true;
1249
- }
1250
- }
1251
- }
1252
-
1253
- const inst = owner.stateNode;
1254
- if (!inst) {
1255
- throw new Error(
1256
- `Missing owner for string ref ${stringRef}. This error is likely caused by a ` +
1257
- 'bug in React. Please file an issue.',
1258
- );
1259
- }
1260
-
1261
- const refs = inst.refs;
1262
- if (value === null) {
1263
- delete refs[stringRef];
1264
- } else {
1265
- refs[stringRef] = value;
1266
- }
1267
-}
1268
-
1269
-function isReactClass(type) {
1270
- return type.prototype && type.prototype.isReactComponent;
1271
-}
packages/shared/ReactFeatureFlags.js
-2
@@ -208,8 +208,6 @@ export const enableFilterEmptyStringAttributesDOM = true;
208
// Disabled caching behavior of `react/cache` in client runtimes.
209
export const disableClientCache = true;
210
211
-export const disableStringRefs = true;
212
-
211
// Warn on any usage of ReactTestRenderer
212
export const enableReactTestRendererWarning = true;
213
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -41,7 +41,6 @@ export const disableLegacyContext = false;
41
export const disableLegacyContextForFunctionComponents = false;
42
export const disableLegacyMode = false;
43
export const disableSchedulerTimeoutInWorkLoop = false;
44
-export const disableStringRefs = true;
44
export const disableTextareaChildren = false;
45
export const enableAsyncActions = true;
46
export const enableAsyncDebugInfo = false;
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -30,7 +30,6 @@ export const disableLegacyContext = true;
30
export const disableLegacyContextForFunctionComponents = true;
31
export const disableLegacyMode = false;
32
export const disableSchedulerTimeoutInWorkLoop = false;
33
-export const disableStringRefs = true;
33
export const disableTextareaChildren = false;
34
export const enableAsyncActions = true;
35
export const enableAsyncDebugInfo = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -90,7 +90,6 @@ export const enableSiblingPrerendering = false;
90
// We really need to get rid of this whole module. Any test renderer specific
91
// flags should be handled by the Fiber config.
92
// const __NEXT_MAJOR__ = __EXPERIMENTAL__;
93
-export const disableStringRefs = true;
93
export const disableLegacyMode = true;
94
export const disableLegacyContext = true;
95
export const disableLegacyContextForFunctionComponents = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js
-1
@@ -22,7 +22,6 @@ export const disableLegacyContext = false;
22
export const disableLegacyContextForFunctionComponents = false;
23
export const disableLegacyMode = false;
24
export const disableSchedulerTimeoutInWorkLoop = false;
25
-export const disableStringRefs = true;
25
export const disableTextareaChildren = false;
26
export const enableAsyncActions = true;
27
export const enableAsyncDebugInfo = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-2
@@ -82,8 +82,6 @@ export const disableClientCache = true;
82
export const enableServerComponentLogs = true;
83
export const enableInfiniteRenderLoopDetection = false;
84
85
-export const disableStringRefs = true;
86
-
85
export const enableReactTestRendererWarning = false;
86
export const disableLegacyMode = true;
87
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -52,7 +52,6 @@ export const enableSuspenseAvoidThisFallback = true;
52
export const enableSuspenseAvoidThisFallbackFizz = false;
53
54
export const disableIEWorkarounds = true;
55
-export const disableStringRefs = true;
55
export const enableCPUSuspense = true;
56
export const enableUseMemoCacheHook = true;
57
export const enableUseEffectEventHook = true;