@samitouri / QOS-React-1 / commits / e1378902bb

[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;