From 03353401a122274bd4a57f1b5bbde08607650981 Mon Sep 17 00:00:00 2001 From: Jan Kassens Date: Wed, 6 Nov 2024 13:30:33 -0500 Subject: [PATCH] [string-refs] cleanup string ref code --- packages/jest-react/src/JestReact.js | 11 - .../react-client/src/ReactFlightClient.js | 14 - .../src/__tests__/ReactFlight-test.js | 4 +- .../src/__tests__/ReactComponent-test.js | 112 ------- .../__tests__/ReactCompositeComponent-test.js | 7 +- .../ReactDOMServerIntegrationRefs-test.js | 38 +-- .../ReactDeprecationWarnings-test.js | 117 ------- .../__tests__/ReactFunctionComponent-test.js | 18 -- .../multiple-copies-of-react-test.js | 38 --- packages/react-dom/src/__tests__/refs-test.js | 301 ------------------ .../src/createReactNoop.js | 12 +- .../src/ReactFiberAsyncDispatcher.js | 4 +- .../src/ReactFiberBeginWork.js | 22 +- .../src/ReactFiberCommitEffects.js | 3 +- .../src/ReactFiberWorkLoop.js | 15 +- .../src/ReactInternalTypes.js | 2 +- .../src/__tests__/ReactFiberRefs-test.js | 29 -- .../ReactIncrementalSideEffects-test.js | 34 -- .../src/__tests__/ReactFlightDOMEdge-test.js | 4 +- .../src/ReactFizzAsyncDispatcher.js | 6 - packages/react-server/src/ReactFizzServer.js | 3 +- .../src/flight/ReactFlightAsyncDispatcher.js | 10 - .../ReactCoffeeScriptClass-test.coffee | 21 -- .../src/__tests__/ReactCreateElement-test.js | 2 +- .../react/src/__tests__/ReactES6Class-test.js | 21 -- .../src/__tests__/ReactElementClone-test.js | 68 +--- .../src/__tests__/ReactStrictMode-test.js | 49 --- .../__tests__/ReactTypeScriptClass-test.ts | 16 - packages/react/src/jsx/ReactJSXElement.js | 205 +----------- packages/shared/ReactFeatureFlags.js | 2 - .../forks/ReactFeatureFlags.native-fb.js | 1 - .../forks/ReactFeatureFlags.native-oss.js | 1 - .../forks/ReactFeatureFlags.test-renderer.js | 1 - ...actFeatureFlags.test-renderer.native-fb.js | 1 - .../ReactFeatureFlags.test-renderer.www.js | 2 - .../shared/forks/ReactFeatureFlags.www.js | 1 - 36 files changed, 35 insertions(+), 1160 deletions(-) delete mode 100644 packages/react-dom/src/__tests__/multiple-copies-of-react-test.js diff --git a/packages/jest-react/src/JestReact.js b/packages/jest-react/src/JestReact.js index 8fcec97f63f7..3cb9ab88f32f 100644 --- a/packages/jest-react/src/JestReact.js +++ b/packages/jest-react/src/JestReact.js @@ -6,7 +6,6 @@ */ import {REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols'; -import {disableStringRefs} from 'shared/ReactFeatureFlags'; const {assertConsoleLogsCleared} = require('internal-test-utils/consoleMock'); import isArray from 'shared/isArray'; @@ -56,14 +55,6 @@ function createJSXElementForTestComparison(type, props) { value: null, }); return element; - } else if (!__DEV__ && disableStringRefs) { - return { - $$typeof: REACT_ELEMENT_TYPE, - type: type, - key: null, - ref: null, - props: props, - }; } else { return { $$typeof: REACT_ELEMENT_TYPE, @@ -71,8 +62,6 @@ function createJSXElementForTestComparison(type, props) { key: null, ref: null, props: props, - _owner: null, - _store: __DEV__ ? {} : undefined, }; } } diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 6c370bd2ef65..9a3d3e019867 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -41,7 +41,6 @@ import type {Postpone} from 'react/src/ReactPostpone'; import type {TemporaryReferenceSet} from './ReactFlightTemporaryReferences'; import { - disableStringRefs, enableBinaryFlight, enablePostpone, enableFlightReadableStream, @@ -688,16 +687,6 @@ function createElement( enumerable: false, get: nullRefGetter, }); - } else if (!__DEV__ && disableStringRefs) { - element = ({ - // This tag allows us to uniquely identify this as a React Element - $$typeof: REACT_ELEMENT_TYPE, - - type, - key, - ref: null, - props, - }: any); } else { element = ({ // This tag allows us to uniquely identify this as a React Element @@ -707,9 +696,6 @@ function createElement( key, ref: null, props, - - // Record the component responsible for creating this element. - _owner: __DEV__ && owner === null ? response._debugRootOwner : owner, }: any); } diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js index 47c6db808f04..dc33d03f9426 100644 --- a/packages/react-client/src/__tests__/ReactFlight-test.js +++ b/packages/react-client/src/__tests__/ReactFlight-test.js @@ -3268,9 +3268,7 @@ describe('ReactFlight', () => { expect(greeting._owner).toBe(greeting._debugInfo[0]); } else { expect(greeting._debugInfo).toBe(undefined); - expect(greeting._owner).toBe( - gate(flags => flags.disableStringRefs) ? undefined : null, - ); + expect(greeting._owner).toBe(undefined); } ReactNoop.render(greeting); }); diff --git a/packages/react-dom/src/__tests__/ReactComponent-test.js b/packages/react-dom/src/__tests__/ReactComponent-test.js index 9b2f443a7c34..645911469754 100644 --- a/packages/react-dom/src/__tests__/ReactComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactComponent-test.js @@ -42,19 +42,6 @@ describe('ReactComponent', () => { }).toThrowError(/Target container is not a DOM element./); }); - // @gate !disableStringRefs - it('should throw when supplying a string ref outside of render method', async () => { - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - await expect( - act(() => { - root.render(
); - }), - // TODO: This throws an AggregateError. Need to update test infra to - // support matching against AggregateError. - ).rejects.toThrow(); - }); - it('should throw (in dev) when children are mutated during render', async () => { function Wrapper(props) { props.children[1] =

; // Mutation is illegal @@ -132,105 +119,6 @@ describe('ReactComponent', () => { } }); - // @gate !disableStringRefs - it('string refs do not detach and reattach on every render', async () => { - let refVal; - class Child extends React.Component { - componentDidUpdate() { - // The parent ref should still be attached because it hasn't changed - // since the last render. If the ref had changed, then this would be - // undefined because refs are attached during the same phase (layout) - // as componentDidUpdate, in child -> parent order. So the new parent - // ref wouldn't have attached yet. - refVal = this.props.contextRef(); - } - - render() { - if (this.props.show) { - return

child
; - } - } - } - - class Parent extends React.Component { - render() { - return ( -
- this.refs.root} - show={this.props.showChild} - /> -
- ); - } - } - - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - - await act(() => { - root.render(); - }); - - assertConsoleErrorDev(['contains the string ref']); - - expect(refVal).toBe(undefined); - await act(() => { - root.render(); - }); - expect(refVal).toBe(container.querySelector('#test-root')); - }); - - // @gate !disableStringRefs - it('should support string refs on owned components', async () => { - const innerObj = {}; - const outerObj = {}; - - class Wrapper extends React.Component { - getObject = () => { - return this.props.object; - }; - - render() { - return
{this.props.children}
; - } - } - - class Component extends React.Component { - render() { - const inner = ; - const outer = ( - - {inner} - - ); - return outer; - } - - componentDidMount() { - expect(this.refs.inner.getObject()).toEqual(innerObj); - expect(this.refs.outer.getObject()).toEqual(outerObj); - } - } - - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - await expect(async () => { - await act(() => { - root.render(); - }); - }).toErrorDev([ - 'Component "Component" contains the string ref "inner". ' + - 'Support for string refs will be removed in a future major release. ' + - 'We recommend using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' + - ' in Wrapper (at **)\n' + - ' in div (at **)\n' + - ' in Wrapper (at **)\n' + - ' in Component (at **)', - ]); - }); - it('should not have string refs on unmounted components', async () => { class Parent extends React.Component { render() { diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js index 89d0b83abc3b..444407668120 100644 --- a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js @@ -537,11 +537,8 @@ describe('ReactCompositeComponent', () => { }); it('should cleanup even if render() fatals', async () => { - const dispatcherEnabled = - __DEV__ || - !gate(flags => flags.disableStringRefs) || - gate(flags => flags.enableCache); - const ownerEnabled = __DEV__ || !gate(flags => flags.disableStringRefs); + const dispatcherEnabled = __DEV__ || gate(flags => flags.enableCache); + const ownerEnabled = __DEV__; let stashedDispatcher; class BadComponent extends React.Component { diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationRefs-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationRefs-test.js index b8d1a0716ae5..9094af348472 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationRefs-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationRefs-test.js @@ -29,12 +29,8 @@ function initModules() { }; } -const { - resetModules, - asyncReactDOMRender, - clientRenderOnServerString, - expectMarkupMatch, -} = ReactDOMServerIntegrationUtils(initModules); +const {resetModules, clientRenderOnServerString, expectMarkupMatch} = + ReactDOMServerIntegrationUtils(initModules); describe('ReactDOMServerIntegration', () => { beforeEach(() => { @@ -75,36 +71,6 @@ describe('ReactDOMServerIntegration', () => { expect(refElement).not.toBe(null); expect(refElement).toBe(e); }); - - // @gate !disableStringRefs - it('should have string refs on client when rendered over server markup', async () => { - class RefsComponent extends React.Component { - render() { - return
; - } - } - - const markup = ReactDOMServer.renderToString(); - const root = document.createElement('div'); - root.innerHTML = markup; - let component = null; - resetModules(); - await expect(async () => { - await asyncReactDOMRender( - (component = e)} />, - root, - true, - ); - }).toErrorDev([ - 'Component "RefsComponent" contains the string ref "myDiv". ' + - 'Support for string refs will be removed in a future major release. ' + - 'We recommend using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' + - ' in div (at **)\n' + - ' in RefsComponent (at **)', - ]); - expect(component.refs.myDiv).toBe(root.firstChild); - }); }); it('should forward refs', async () => { diff --git a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js b/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js index 77c9e1886e0a..e0022dd99004 100644 --- a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js +++ b/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js @@ -11,7 +11,6 @@ let React; let ReactNoop; -let JSXDEVRuntime; let waitForAll; describe('ReactDeprecationWarnings', () => { @@ -21,9 +20,6 @@ describe('ReactDeprecationWarnings', () => { ReactNoop = require('react-noop-renderer'); const InternalTestUtils = require('internal-test-utils'); waitForAll = InternalTestUtils.waitForAll; - if (__DEV__) { - JSXDEVRuntime = require('react/jsx-dev-runtime'); - } }); // @gate !disableDefaultPropsExceptForClasses || !__DEV__ @@ -65,117 +61,4 @@ describe('ReactDeprecationWarnings', () => { 'release. Use JavaScript default parameters instead.', ); }); - - // @gate !disableStringRefs - it('should warn when given string refs', async () => { - class RefComponent extends React.Component { - render() { - return null; - } - } - class Component extends React.Component { - render() { - return ; - } - } - - ReactNoop.render(); - await expect(async () => await waitForAll([])).toErrorDev( - 'Component "Component" contains the string ref "refComponent". ' + - 'Support for string refs will be removed in a future major release. ' + - 'We recommend using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-string-ref' + - '\n in RefComponent (at **)' + - '\n in Component (at **)', - ); - }); - - // Disabling this until #28732 lands so we can assert on the warning message. - // (It's already disabled in all but the Meta builds, anyway. Nbd.) - // @gate TODO || !__DEV__ - // @gate !disableStringRefs - it('should warn when owner and self are the same for string refs', async () => { - class RefComponent extends React.Component { - render() { - return null; - } - } - class Component extends React.Component { - render() { - return React.createElement(RefComponent, { - ref: 'refComponent', - __self: this, - }); - } - } - - ReactNoop.render(); - await expect(async () => await waitForAll([])).toErrorDev([ - 'Component "Component" contains the string ref "refComponent". Support for string refs will be removed in a future major release.', - ]); - await waitForAll([]); - }); - - // Disabling this until #28732 lands so we can assert on the warning message. - // (It's already disabled in all but the Meta builds, anyway. Nbd.) - // @gate TODO || !__DEV__ - // @gate !disableStringRefs - it('should warn when owner and self are different for string refs (createElement)', async () => { - class RefComponent extends React.Component { - render() { - return null; - } - } - class Component extends React.Component { - render() { - return React.createElement(RefComponent, { - ref: 'refComponent', - __self: {}, - }); - } - } - - ReactNoop.render(); - await expect(async () => await waitForAll([])).toErrorDev([ - 'Component "Component" contains the string ref "refComponent". ' + - 'Support for string refs will be removed in a future major release. ' + - 'This case cannot be automatically converted to an arrow function. ' + - 'We ask you to manually fix this case by using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-string-ref', - ]); - }); - - // @gate __DEV__ - // @gate !disableStringRefs - it('should warn when owner and self are different for string refs (jsx)', async () => { - class RefComponent extends React.Component { - render() { - return null; - } - } - class Component extends React.Component { - render() { - return JSXDEVRuntime.jsxDEV( - RefComponent, - {ref: 'refComponent'}, - null, - false, - {}, - {}, - ); - } - } - - ReactNoop.render(); - await expect(async () => await waitForAll([])).toErrorDev([ - 'Component "Component" contains the string ref "refComponent". ' + - 'Support for string refs will be removed in a future major release. ' + - 'This case cannot be automatically converted to an arrow function. ' + - 'We ask you to manually fix this case by using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-string-ref', - ]); - }); }); diff --git a/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js b/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js index 24f97520ecc3..f1250ce26d7c 100644 --- a/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js @@ -179,24 +179,6 @@ describe('ReactFunctionComponent', () => { ).resolves.not.toThrowError(); }); - // @gate !disableStringRefs - it('should throw on string refs in pure functions', async () => { - function Child() { - return
; - } - - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - await expect( - act(() => { - root.render(); - }), - ) - // TODO: This throws an AggregateError. Need to update test infra to - // support matching against AggregateError. - .rejects.toThrowError(); - }); - it('should use correct name in key warning', async () => { function Child() { return
{[]}
; diff --git a/packages/react-dom/src/__tests__/multiple-copies-of-react-test.js b/packages/react-dom/src/__tests__/multiple-copies-of-react-test.js deleted file mode 100644 index 2d107cbb1970..000000000000 --- a/packages/react-dom/src/__tests__/multiple-copies-of-react-test.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @emails react-core - */ - -'use strict'; - -let React = require('react'); -const ReactDOMClient = require('react-dom/client'); -const act = require('internal-test-utils').act; - -class TextWithStringRef extends React.Component { - render() { - jest.resetModules(); - React = require('react'); - return Hello world!; - } -} - -describe('when different React version is used with string ref', () => { - // @gate !disableStringRefs - it('throws the "Refs must have owner" warning', async () => { - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - await expect( - act(() => { - root.render(); - }), - ) - // TODO: This throws an AggregateError. Need to update test infra to - // support matching against AggregateError. - .rejects.toThrow(); - }); -}); diff --git a/packages/react-dom/src/__tests__/refs-test.js b/packages/react-dom/src/__tests__/refs-test.js index 80ea8d1e26b3..b80f8b27753f 100644 --- a/packages/react-dom/src/__tests__/refs-test.js +++ b/packages/react-dom/src/__tests__/refs-test.js @@ -13,179 +13,6 @@ const React = require('react'); const ReactDOMClient = require('react-dom/client'); const act = require('internal-test-utils').act; -// This is testing if string refs are deleted from `instance.refs` -// Once support for string refs is removed, this test can be removed. -// Detaching is already tested in refs-detruction-test.js -describe('reactiverefs', () => { - let container; - - afterEach(() => { - if (container) { - document.body.removeChild(container); - container = null; - } - }); - - /** - * Counts clicks and has a renders an item for each click. Each item rendered - * has a ref of the form "clickLogN". - */ - class ClickCounter extends React.Component { - state = {count: this.props.initialCount}; - - triggerReset = () => { - this.setState({count: this.props.initialCount}); - }; - - handleClick = () => { - this.setState({count: this.state.count + 1}); - }; - - render() { - const children = []; - let i; - for (i = 0; i < this.state.count; i++) { - children.push( -
, - ); - } - return ( - - {children} - - ); - } - } - - const expectClickLogsLengthToBe = function (instance, length) { - const clickLogs = instance.container.querySelectorAll('.clickLogDiv'); - expect(clickLogs.length).toBe(length); - expect(Object.keys(instance.refs.myCounter.refs).length).toBe(length); - }; - - /** - * Render a TestRefsComponent and ensure that the main refs are wired up. - */ - const renderTestRefsComponent = async function () { - /** - * Only purpose is to test that refs are tracked even when applied to a - * component that is injected down several layers. Ref systems are difficult to - * build in such a way that ownership is maintained in an airtight manner. - */ - class GeneralContainerComponent extends React.Component { - render() { - return
{this.props.children}
; - } - } - - /** - * Notice how refs ownership is maintained even when injecting a component - * into a different parent. - */ - class TestRefsComponent extends React.Component { - container = null; - doReset = () => { - this.refs.myCounter.triggerReset(); - }; - - render() { - return ( -
(this.container = current)}> -
- Reset Me By Clicking This. -
- - - -
- ); - } - } - - container = document.createElement('div'); - document.body.appendChild(container); - - let testRefsComponent; - await expect(async () => { - const root = ReactDOMClient.createRoot(container); - await act(() => { - root.render( - { - testRefsComponent = current; - }} - />, - ); - }); - }).toErrorDev([ - 'Component "TestRefsComponent" contains the string ' + - 'ref "resetDiv". Support for string refs will be removed in a ' + - 'future major release. We recommend using useRef() or createRef() ' + - 'instead. Learn more about using refs safely ' + - 'here: https://react.dev/link/strict-mode-string-ref\n' + - ' in div (at **)\n' + - ' in div (at **)\n' + - ' in TestRefsComponent (at **)', - 'Component "ClickCounter" contains the string ' + - 'ref "clickLog0". Support for string refs will be removed in a ' + - 'future major release. We recommend using useRef() or createRef() ' + - 'instead. Learn more about using refs safely ' + - 'here: https://react.dev/link/strict-mode-string-ref\n' + - ' in div (at **)\n' + - ' in span (at **)\n' + - ' in ClickCounter (at **)', - ]); - - expect(testRefsComponent instanceof TestRefsComponent).toBe(true); - - const generalContainer = testRefsComponent.refs.myContainer; - expect(generalContainer instanceof GeneralContainerComponent).toBe(true); - - const counter = testRefsComponent.refs.myCounter; - expect(counter instanceof ClickCounter).toBe(true); - - return testRefsComponent; - }; - - /** - * Ensure that for every click log there is a corresponding ref (from the - * perspective of the injected ClickCounter component. - */ - // @gate !disableStringRefs - it('Should increase refs with an increase in divs', async () => { - const testRefsComponent = await renderTestRefsComponent(); - const clickIncrementer = - testRefsComponent.container.querySelector('.clickIncrementer'); - - expectClickLogsLengthToBe(testRefsComponent, 1); - - // After clicking the reset, there should still only be one click log ref. - testRefsComponent.refs.resetDiv.click(); - expectClickLogsLengthToBe(testRefsComponent, 1); - - // Begin incrementing clicks (and therefore refs). - await act(() => { - clickIncrementer.click(); - }); - expectClickLogsLengthToBe(testRefsComponent, 2); - - await act(() => { - clickIncrementer.click(); - }); - expectClickLogsLengthToBe(testRefsComponent, 3); - - // Now reset again - await act(() => { - testRefsComponent.refs.resetDiv.click(); - }); - expectClickLogsLengthToBe(testRefsComponent, 1); - }); -}); - /** * Tests that when a ref hops around children, we can track that correctly. */ @@ -320,32 +147,6 @@ describe('ref swapping', () => { expect(refCalled).toBe(1); }); - // @gate !disableStringRefs - it('coerces numbers to strings', async () => { - class A extends React.Component { - render() { - return
; - } - } - let a; - await expect(async () => { - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - - await act(() => { - root.render( (a = current)} />); - }); - }).toErrorDev([ - 'Component "A" contains the string ref "1". ' + - 'Support for string refs will be removed in a future major release. ' + - 'We recommend using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' + - ' in div (at **)\n' + - ' in A (at **)', - ]); - expect(a.refs[1].nodeName).toBe('DIV'); - }); - it('provides an error for invalid refs', async () => { const container = document.createElement('div'); const root = ReactDOMClient.createRoot(container); @@ -469,108 +270,6 @@ describe('root level refs', () => { }); }); -describe('creating element with string ref in constructor', () => { - class RefTest extends React.Component { - constructor(props) { - super(props); - this.p =

Hello!

; - } - - render() { - return
{this.p}
; - } - } - - // @gate !disableStringRefs && !__DEV__ - it('throws an error in prod', async () => { - await expect(async function () { - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - - await act(() => { - root.render(); - }); - }) - // TODO: This throws an AggregateError. Need to update test infra to - // support matching against AggregateError. - .rejects.toThrowError(); - }); -}); - -describe('strings refs across renderers', () => { - // @gate !disableStringRefs - it('does not break', async () => { - class Parent extends React.Component { - render() { - // This component owns both refs. - return ( - } - child2={
} - /> - ); - } - } - - class Indirection extends React.Component { - componentDidUpdate() { - // One ref is being rendered later using another renderer copy. - jest.resetModules(); - const AnotherCopyOfReactDOM = require('react-dom'); - const AnotherCopyOfReactDOMClient = require('react-dom/client'); - const root = AnotherCopyOfReactDOMClient.createRoot(div2); - AnotherCopyOfReactDOM.flushSync(() => { - root.render(this.props.child2); - }); - } - render() { - // The other one is being rendered directly. - return this.props.child1; - } - } - - const div1 = document.createElement('div'); - const div2 = document.createElement('div'); - - const root = ReactDOMClient.createRoot(div1); - let inst; - await expect(async () => { - await act(() => { - root.render( - { - if (current !== null) { - inst = current; - } - }} - />, - ); - }); - }).toErrorDev([ - 'Component "Parent" contains the string ref "child1". ' + - 'Support for string refs will be removed in a future major release. ' + - 'We recommend using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' + - ' in div (at **)\n' + - ' in Indirection (at **)\n' + - ' in Parent (at **)', - ]); - - // Only the first ref has rendered yet. - expect(inst.refs.child1.tagName).toBe('DIV'); - expect(inst.refs.child1).toBe(div1.firstChild); - - // Now both refs should be rendered. - await act(() => { - root.render(); - }); - expect(inst.refs.child1.tagName).toBe('DIV'); - expect(inst.refs.child1).toBe(div1.firstChild); - expect(inst.refs.child2.tagName).toBe('DIV'); - expect(inst.refs.child2).toBe(div2.firstChild); - }); -}); - describe('refs return clean up function', () => { it('calls clean up function if it exists', async () => { const container = document.createElement('div'); diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 3e95e537e9dd..4c1bf05e5404 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -35,7 +35,7 @@ import { ConcurrentRoot, LegacyRoot, } from 'react-reconciler/constants'; -import {disableLegacyMode, disableStringRefs} from 'shared/ReactFeatureFlags'; +import {disableLegacyMode} from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import ReactVersion from 'shared/ReactVersion'; @@ -843,14 +843,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { value: null, }); return element; - } else if (!__DEV__ && disableStringRefs) { - return { - $$typeof: REACT_ELEMENT_TYPE, - type: type, - key: null, - ref: null, - props: props, - }; } else { return { $$typeof: REACT_ELEMENT_TYPE, @@ -858,8 +850,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { key: null, ref: null, props: props, - _owner: null, - _store: __DEV__ ? {} : undefined, }; } } diff --git a/packages/react-reconciler/src/ReactFiberAsyncDispatcher.js b/packages/react-reconciler/src/ReactFiberAsyncDispatcher.js index d329fb369cae..0d6856f5d0ac 100644 --- a/packages/react-reconciler/src/ReactFiberAsyncDispatcher.js +++ b/packages/react-reconciler/src/ReactFiberAsyncDispatcher.js @@ -14,8 +14,6 @@ import {enableCache} from 'shared/ReactFeatureFlags'; import {readContext} from './ReactFiberNewContext'; import {CacheContext} from './ReactFiberCacheComponent'; -import {disableStringRefs} from 'shared/ReactFeatureFlags'; - import {current as currentOwner} from './ReactCurrentFiber'; function getCacheForType(resourceType: () => T): T { @@ -35,7 +33,7 @@ export const DefaultAsyncDispatcher: AsyncDispatcher = ({ getCacheForType, }: any); -if (__DEV__ || !disableStringRefs) { +if (__DEV__) { DefaultAsyncDispatcher.getOwner = (): null | Fiber => { return currentOwner; }; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index efb34826c970..0a5457d20c33 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -110,7 +110,6 @@ import { enableRenderableContext, disableLegacyMode, disableDefaultPropsExceptForClasses, - disableStringRefs, enableOwnerStacks, } from 'shared/ReactFeatureFlags'; import isArray from 'shared/isArray'; @@ -1052,25 +1051,6 @@ function markRef(current: Fiber | null, workInProgress: Fiber) { ); } if (current === null || current.ref !== ref) { - if (!disableStringRefs && current !== null) { - const oldRef = current.ref; - const newRef = ref; - if ( - typeof oldRef === 'function' && - typeof newRef === 'function' && - typeof oldRef.__stringRef === 'string' && - oldRef.__stringRef === newRef.__stringRef && - oldRef.__stringRefType === newRef.__stringRefType && - oldRef.__stringRefOwner === newRef.__stringRefOwner - ) { - // Although this is a different callback, it represents the same - // string ref. To avoid breaking old Meta code that relies on string - // refs only being attached once, reuse the old ref. This will - // prevent us from detaching and reattaching the ref on each update. - workInProgress.ref = oldRef; - return; - } - } // Schedule a Ref effect workInProgress.flags |= Ref | RefStatic; } @@ -1388,7 +1368,7 @@ function finishClassComponent( const instance = workInProgress.stateNode; // Rerender - if (__DEV__ || !disableStringRefs) { + if (__DEV__) { setCurrentFiber(workInProgress); } let nextChildren; diff --git a/packages/react-reconciler/src/ReactFiberCommitEffects.js b/packages/react-reconciler/src/ReactFiberCommitEffects.js index 4d10c9ee6faf..70c49bc62fae 100644 --- a/packages/react-reconciler/src/ReactFiberCommitEffects.js +++ b/packages/react-reconciler/src/ReactFiberCommitEffects.js @@ -18,7 +18,6 @@ import { enableProfilerNestedUpdatePhase, enableSchedulingProfiler, enableScopeAPI, - disableStringRefs, } from 'shared/ReactFeatureFlags'; import { ClassComponent, @@ -773,7 +772,7 @@ function commitAttachRef(finishedWork: Fiber) { if (__DEV__) { // TODO: We should move these warnings to happen during the render // phase (markRef). - if (disableStringRefs && typeof ref === 'string') { + if (typeof ref === 'string') { console.error('String refs are no longer supported.'); } else if (!ref.hasOwnProperty('current')) { console.error( diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index aa5884444063..1ea05dd010a8 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -40,7 +40,6 @@ import { enableInfiniteRenderLoopDetection, disableLegacyMode, disableDefaultPropsExceptForClasses, - disableStringRefs, enableSiblingPrerendering, enableComponentPerformanceTrack, } from 'shared/ReactFeatureFlags'; @@ -1732,7 +1731,7 @@ function handleThrow(root: FiberRoot, thrownValue: any): void { // These should be reset immediately because they're only supposed to be set // when React is executing user code. resetHooksAfterThrow(); - if (__DEV__ || !disableStringRefs) { + if (__DEV__) { resetCurrentFiber(); } @@ -1928,7 +1927,7 @@ function popDispatcher(prevDispatcher: any) { } function pushAsyncDispatcher() { - if (enableCache || __DEV__ || !disableStringRefs) { + if (enableCache || __DEV__) { const prevAsyncDispatcher = ReactSharedInternals.A; ReactSharedInternals.A = DefaultAsyncDispatcher; return prevAsyncDispatcher; @@ -1938,7 +1937,7 @@ function pushAsyncDispatcher() { } function popAsyncDispatcher(prevAsyncDispatcher: any) { - if (enableCache || __DEV__ || !disableStringRefs) { + if (enableCache || __DEV__) { ReactSharedInternals.A = prevAsyncDispatcher; } } @@ -2497,9 +2496,6 @@ function performUnitOfWork(unitOfWork: Fiber): void { } } - if (!disableStringRefs) { - resetCurrentFiber(); - } unitOfWork.memoizedProps = unitOfWork.pendingProps; if (next === null) { // If this doesn't spawn new work, complete the current work. @@ -2519,11 +2515,6 @@ function replaySuspendedUnitOfWork(unitOfWork: Fiber): void { next = replayBeginWork(unitOfWork); } - // The begin phase finished successfully without suspending. Return to the - // normal work loop. - if (!disableStringRefs) { - resetCurrentFiber(); - } unitOfWork.memoizedProps = unitOfWork.pendingProps; if (next === null) { // If this doesn't spawn new work, complete the current work. diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 1c98a9e9c7fb..d91727525c96 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -460,6 +460,6 @@ export type Dispatcher = { export type AsyncDispatcher = { getCacheForType: (resourceType: () => T) => T, - // DEV-only (or !disableStringRefs) + // DEV-only getOwner: () => null | Fiber | ReactComponentInfo | ComponentStackNode, }; diff --git a/packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js b/packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js index 8fd5206c94ae..78409c8e2a4e 100644 --- a/packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js +++ b/packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js @@ -85,35 +85,6 @@ describe('ReactFiberRefs', () => { expect(ref2.current).not.toBe(null); }); - // @gate !disableStringRefs - it('string ref props are converted to function refs', async () => { - let refProp; - function Child({ref}) { - refProp = ref; - return
; - } - - let owner; - class Owner extends React.Component { - render() { - owner = this; - return ; - } - } - - const root = ReactNoop.createRoot(); - await act(() => root.render()); - - // When string refs aren't disabled, string refs - // the receiving component receives a callback ref, not the original string. - // This behavior should never be shipped to open source; it's only here to - // allow Meta to keep using string refs temporarily while they finish - // migrating their codebase. - expect(typeof refProp === 'function').toBe(true); - expect(owner.refs.child.type).toBe('div'); - }); - - // @gate disableStringRefs it('throw if a string ref is passed to a ref-receiving component', async () => { let refProp; function Child({ref}) { diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js index 81f892b48e35..c4473c1774e9 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js @@ -1334,38 +1334,4 @@ describe('ReactIncrementalSideEffects', () => { // TODO: Test that mounts, updates, refs, unmounts and deletions happen in the // expected way for aborted and resumed render life-cycles. - - // @gate !disableStringRefs - it('supports string refs', async () => { - let fooInstance = null; - - class Bar extends React.Component { - componentDidMount() { - this.test = 'test'; - } - render() { - return
; - } - } - - class Foo extends React.Component { - render() { - fooInstance = this; - return ; - } - } - - ReactNoop.render(); - await expect(async () => { - await waitForAll([]); - }).toErrorDev([ - 'Component "Foo" contains the string ref "bar". ' + - 'Support for string refs will be removed in a future major release. ' + - 'We recommend using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' + - ' in Bar (at **)\n' + - ' in Foo (at **)', - ]); - expect(fooInstance.refs.bar.test).toEqual('test'); - }); }); diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js index ed349cde81f0..1a097ffbda56 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js @@ -1001,9 +1001,7 @@ describe('ReactFlightDOMEdge', () => { expect(greeting._owner).toBe(lazyWrapper._debugInfo[0]); } else { expect(lazyWrapper._debugInfo).toBe(undefined); - expect(greeting._owner).toBe( - gate(flags => flags.disableStringRefs) ? undefined : null, - ); + expect(greeting._owner).toBe(undefined); } }); diff --git a/packages/react-server/src/ReactFizzAsyncDispatcher.js b/packages/react-server/src/ReactFizzAsyncDispatcher.js index 3a548ae13803..e4d940d463c2 100644 --- a/packages/react-server/src/ReactFizzAsyncDispatcher.js +++ b/packages/react-server/src/ReactFizzAsyncDispatcher.js @@ -10,8 +10,6 @@ import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes'; import type {ComponentStackNode} from './ReactFizzComponentStack'; -import {disableStringRefs} from 'shared/ReactFeatureFlags'; - import {currentTaskInDEV} from './ReactFizzCurrentTask'; function getCacheForType(resourceType: () => T): T { @@ -29,8 +27,4 @@ if (__DEV__) { } return currentTaskInDEV.componentStack; }; -} else if (!disableStringRefs) { - DefaultAsyncDispatcher.getOwner = (): null => { - return null; - }; } diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index 7b881fdbe8cc..f245861ba001 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -162,7 +162,6 @@ import { enableRenderableContext, disableDefaultPropsExceptForClasses, enableAsyncIterableChildren, - disableStringRefs, enableOwnerStacks, } from 'shared/ReactFeatureFlags'; @@ -4452,7 +4451,7 @@ export function performWork(request: Request): void { const prevDispatcher = ReactSharedInternals.H; ReactSharedInternals.H = HooksDispatcher; let prevAsyncDispatcher = null; - if (enableCache || __DEV__ || !disableStringRefs) { + if (enableCache || __DEV__) { prevAsyncDispatcher = ReactSharedInternals.A; ReactSharedInternals.A = DefaultAsyncDispatcher; } diff --git a/packages/react-server/src/flight/ReactFlightAsyncDispatcher.js b/packages/react-server/src/flight/ReactFlightAsyncDispatcher.js index f5f031a860ff..00c1abf33292 100644 --- a/packages/react-server/src/flight/ReactFlightAsyncDispatcher.js +++ b/packages/react-server/src/flight/ReactFlightAsyncDispatcher.js @@ -7,14 +7,9 @@ * @flow */ -import type {ReactComponentInfo} from 'shared/ReactTypes'; - import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes'; import {resolveRequest, getCache} from '../ReactFlightServer'; - -import {disableStringRefs} from 'shared/ReactFeatureFlags'; - import {resolveOwner} from './ReactFlightCurrentOwner'; function resolveCache(): Map { @@ -40,9 +35,4 @@ export const DefaultAsyncDispatcher: AsyncDispatcher = ({ if (__DEV__) { DefaultAsyncDispatcher.getOwner = resolveOwner; -} else if (!disableStringRefs) { - // Server Components never use string refs but the JSX runtime looks for it. - DefaultAsyncDispatcher.getOwner = (): null | ReactComponentInfo => { - return null; - }; } diff --git a/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee b/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee index 22ef789f4f6e..3ad6aa023518 100644 --- a/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee +++ b/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee @@ -551,25 +551,4 @@ describe 'ReactCoffeeScriptClass', -> ], ) - if !featureFlags.disableStringRefs - it 'supports string refs', -> - class Foo extends React.Component - render: -> - React.createElement(InnerComponent, - name: 'foo' - ref: 'inner' - ) - - ref = React.createRef() - expect(-> - test(React.createElement(Foo, ref: ref), 'DIV', 'foo') - ).toErrorDev([ - 'Component "Foo" contains the string ref "inner". ' + - 'Support for string refs will be removed in a future major release. ' + - 'We recommend using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' + - ' in _Class (at **)' - ]); - expect(ref.current.refs.inner.getName()).toBe 'foo' - undefined diff --git a/packages/react/src/__tests__/ReactCreateElement-test.js b/packages/react/src/__tests__/ReactCreateElement-test.js index 3ab5c34c3304..392f89979e76 100644 --- a/packages/react/src/__tests__/ReactCreateElement-test.js +++ b/packages/react/src/__tests__/ReactCreateElement-test.js @@ -218,7 +218,7 @@ describe('ReactCreateElement', () => { } const root = ReactDOMClient.createRoot(document.createElement('div')); await act(() => root.render(React.createElement(Wrapper))); - if (__DEV__ || !gate(flags => flags.disableStringRefs)) { + if (__DEV__) { expect(element._owner.stateNode).toBe(instance); } else { expect('_owner' in element).toBe(false); diff --git a/packages/react/src/__tests__/ReactES6Class-test.js b/packages/react/src/__tests__/ReactES6Class-test.js index 3ac0b18e8e75..eceeab18c5bb 100644 --- a/packages/react/src/__tests__/ReactES6Class-test.js +++ b/packages/react/src/__tests__/ReactES6Class-test.js @@ -592,25 +592,4 @@ describe('ReactES6Class', () => { ]); }); } - - if (!require('shared/ReactFeatureFlags').disableStringRefs) { - it('supports string refs', () => { - class Foo extends React.Component { - render() { - return ; - } - } - const ref = React.createRef(); - expect(() => { - runTest(, 'DIV', 'foo'); - }).toErrorDev([ - 'Component "Foo" contains the string ref "inner". ' + - 'Support for string refs will be removed in a future major release. ' + - 'We recommend using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' + - ' in Inner (at **)', - ]); - expect(ref.current.refs.inner.getName()).toBe('foo'); - }); - } }); diff --git a/packages/react/src/__tests__/ReactElementClone-test.js b/packages/react/src/__tests__/ReactElementClone-test.js index 4e7d0c5ca2a2..fb0bfe2df8d6 100644 --- a/packages/react/src/__tests__/ReactElementClone-test.js +++ b/packages/react/src/__tests__/ReactElementClone-test.js @@ -270,49 +270,8 @@ describe('ReactElementClone', () => { const root = ReactDOMClient.createRoot(document.createElement('div')); await act(() => root.render()); - if (gate(flags => flags.disableStringRefs)) { - expect(component.childRef).toEqual({current: null}); - expect(component.parentRef.current.xyzRef.current.tagName).toBe('SPAN'); - } else if (gate(flags => !flags.disableStringRefs)) { - expect(component.childRef).toEqual({current: null}); - expect(component.parentRef.current.xyzRef.current.tagName).toBe('SPAN'); - } else { - // Not going to bother testing every possible combination. - } - }); - - // @gate !disableStringRefs - it('should steal the ref if a new string ref is specified without an owner', async () => { - // Regression test for this specific feature combination calling cloneElement on an element - // without an owner - await expect(async () => { - // create an element without an owner - const element = React.createElement('div', {id: 'some-id'}); - class Parent extends React.Component { - render() { - return {element}; - } - } - let child; - class Child extends React.Component { - render() { - child = this; - const clone = React.cloneElement(this.props.children, { - ref: 'xyz', - }); - return
{clone}
; - } - } - - const root = ReactDOMClient.createRoot(document.createElement('div')); - await act(() => root.render()); - expect(child.refs.xyz.tagName).toBe('DIV'); - }).toErrorDev([ - 'Component "Child" contains the string ref "xyz". Support for ' + - 'string refs will be removed in a future major release. We recommend ' + - 'using useRef() or createRef() instead. Learn more about using refs ' + - 'safely here: https://react.dev/link/strict-mode-string-ref', - ]); + expect(component.childRef).toEqual({current: null}); + expect(component.parentRef.current.xyzRef.current.tagName).toBe('SPAN'); }); it('should overwrite props', async () => { @@ -403,23 +362,12 @@ describe('ReactElementClone', () => { const clone = React.cloneElement(element, props); expect(clone.type).toBe(ComponentClass); expect(clone.key).toBe('12'); - if (gate(flags => flags.disableStringRefs)) { - expect(clone.props.ref).toBe('34'); - expect(() => expect(clone.ref).toBe('34')).toErrorDev( - 'Accessing element.ref was removed in React 19', - {withoutStack: true}, - ); - expect(clone.props).toEqual({foo: 'ef', ref: '34'}); - } else if (gate(flags => !flags.disableStringRefs)) { - expect(() => { - expect(clone.ref).toBe(element.ref); - }).toErrorDev('Accessing element.ref was removed in React 19', { - withoutStack: true, - }); - expect(clone.props).toEqual({foo: 'ef', ref: element.ref}); - } else { - // Not going to bother testing every possible combination. - } + expect(clone.props.ref).toBe('34'); + expect(() => expect(clone.ref).toBe('34')).toErrorDev( + 'Accessing element.ref was removed in React 19', + {withoutStack: true}, + ); + expect(clone.props).toEqual({foo: 'ef', ref: '34'}); if (__DEV__) { expect(Object.isFrozen(element)).toBe(true); expect(Object.isFrozen(element.props)).toBe(true); diff --git a/packages/react/src/__tests__/ReactStrictMode-test.js b/packages/react/src/__tests__/ReactStrictMode-test.js index 2ab9c3fa18c9..97fcc1ec8c4c 100644 --- a/packages/react/src/__tests__/ReactStrictMode-test.js +++ b/packages/react/src/__tests__/ReactStrictMode-test.js @@ -956,55 +956,6 @@ describe('symbol checks', () => { }); }); -describe('string refs', () => { - beforeEach(() => { - jest.resetModules(); - React = require('react'); - ReactDOM = require('react-dom'); - ReactDOMClient = require('react-dom/client'); - act = require('internal-test-utils').act; - }); - - // @gate !disableStringRefs - it('should warn within a strict tree', async () => { - const {StrictMode} = React; - - class OuterComponent extends React.Component { - render() { - return ( - - - - ); - } - } - - class InnerComponent extends React.Component { - render() { - return null; - } - } - - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - await expect(async () => { - await act(() => { - root.render(); - }); - }).toErrorDev( - 'Component "OuterComponent" contains the string ref "somestring". ' + - 'Support for string refs will be removed in a future major release. ' + - 'We recommend using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' + - ' in InnerComponent (at **)', - ); - - await act(() => { - root.render(); - }); - }); -}); - describe('context legacy', () => { beforeEach(() => { jest.resetModules(); diff --git a/packages/react/src/__tests__/ReactTypeScriptClass-test.ts b/packages/react/src/__tests__/ReactTypeScriptClass-test.ts index 4f4564d356a8..00e3a8b3ff8a 100644 --- a/packages/react/src/__tests__/ReactTypeScriptClass-test.ts +++ b/packages/react/src/__tests__/ReactTypeScriptClass-test.ts @@ -697,20 +697,4 @@ describe('ReactTypeScriptClass', function() { ] ); }); } - - if (!ReactFeatureFlags.disableStringRefs) { - it('supports string refs', function() { - const ref = React.createRef(); - expect(() => { - test(React.createElement(ClassicRefs, {ref: ref}), 'DIV', 'foo'); - }).toErrorDev([ - 'Component "ClassicRefs" contains the string ref "inner". ' + - 'Support for string refs will be removed in a future major release. ' + - 'We recommend using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: https://react.dev/link/strict-mode-string-ref\n' + - ' in Inner (at **)', - ]); - expect(ref.current.refs.inner.getName()).toBe('foo'); - }); - } }); diff --git a/packages/react/src/jsx/ReactJSXElement.js b/packages/react/src/jsx/ReactJSXElement.js index 3722638ad951..e0a689ec2404 100644 --- a/packages/react/src/jsx/ReactJSXElement.js +++ b/packages/react/src/jsx/ReactJSXElement.js @@ -20,13 +20,9 @@ import isValidElementType from 'shared/isValidElementType'; import isArray from 'shared/isArray'; import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame'; import { - disableStringRefs, disableDefaultPropsExceptForClasses, enableOwnerStacks, } from 'shared/ReactFeatureFlags'; -import {checkPropStringCoercion} from 'shared/CheckStringCoercion'; -import {ClassComponent} from 'react-reconciler/src/ReactWorkTags'; -import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber'; const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference'); @@ -59,7 +55,7 @@ function getTaskName(type) { } function getOwner() { - if (__DEV__ || !disableStringRefs) { + if (__DEV__) { const dispatcher = ReactSharedInternals.A; if (dispatcher === null) { return null; @@ -70,17 +66,13 @@ function getOwner() { } let specialPropKeyWarningShown; -let didWarnAboutStringRefs; let didWarnAboutElementRef; let didWarnAboutOldJSXRuntime; if (__DEV__) { - didWarnAboutStringRefs = {}; didWarnAboutElementRef = {}; } -const enableFastJSXWithoutStringRefs = disableStringRefs; - function hasValidRef(config) { if (__DEV__) { if (hasOwnProperty.call(config, 'ref')) { @@ -105,35 +97,6 @@ function hasValidKey(config) { return config.key !== undefined; } -function warnIfStringRefCannotBeAutoConverted(config, self) { - if (__DEV__) { - let owner; - if ( - !disableStringRefs && - typeof config.ref === 'string' && - (owner = getOwner()) && - self && - owner.stateNode !== self - ) { - const componentName = getComponentNameFromType(owner.type); - - if (!didWarnAboutStringRefs[componentName]) { - console.error( - 'Component "%s" contains the string ref "%s". ' + - 'Support for string refs will be removed in a future major release. ' + - 'This case cannot be automatically converted to an arrow function. ' + - 'We ask you to manually fix this case by using useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-string-ref', - getComponentNameFromType(owner.type), - config.ref, - ); - didWarnAboutStringRefs[componentName] = true; - } - } - } -} - function defineKeyPropWarningGetter(props, displayName) { if (__DEV__) { const warnAboutAccessingKey = function () { @@ -259,22 +222,8 @@ function ReactElement( value: null, }); } - } else if (!__DEV__ && disableStringRefs) { - // In prod, `ref` is a regular property and _owner doesn't exist. - element = { - // This tag allows us to uniquely identify this as a React Element - $$typeof: REACT_ELEMENT_TYPE, - - // Built-in properties that belong on the element - type, - key, - ref, - - props, - }; } else { - // In prod, `ref` is a regular property. It will be removed in a - // future release. + // In prod, `ref` is a regular property and _owner doesn't exist. element = { // This tag allows us to uniquely identify this as a React Element $$typeof: REACT_ELEMENT_TYPE, @@ -285,9 +234,6 @@ function ReactElement( ref, props, - - // Record the component responsible for creating this element. - _owner: owner, }; } @@ -368,10 +314,7 @@ export function jsxProd(type, config, maybeKey) { } let props; - if ( - (enableFastJSXWithoutStringRefs || !('ref' in config)) && - !('key' in config) - ) { + if (!('key' in config)) { // If key was not spread in, we can reuse the original props object. This // only works for `jsx`, not `createElement`, because `jsx` is a compiler // target and the compiler always passes a new object. For `createElement`, @@ -390,11 +333,7 @@ export function jsxProd(type, config, maybeKey) { for (const propName in config) { // Skip over reserved prop names if (propName !== 'key') { - if (!disableStringRefs && propName === 'ref') { - props.ref = coerceStringRef(config[propName], getOwner(), type); - } else { - props[propName] = config[propName]; - } + props[propName] = config[propName]; } } } @@ -637,17 +576,8 @@ function jsxDEVImpl( key = '' + config.key; } - if (!disableStringRefs) { - if (hasValidRef(config)) { - warnIfStringRefCannotBeAutoConverted(config, self); - } - } - let props; - if ( - (enableFastJSXWithoutStringRefs || !('ref' in config)) && - !('key' in config) - ) { + if (!('key' in config)) { // If key was not spread in, we can reuse the original props object. This // only works for `jsx`, not `createElement`, because `jsx` is a compiler // target and the compiler always passes a new object. For `createElement`, @@ -666,11 +596,7 @@ function jsxDEVImpl( for (const propName in config) { // Skip over reserved prop names if (propName !== 'key') { - if (!disableStringRefs && propName === 'ref') { - props.ref = coerceStringRef(config[propName], getOwner(), type); - } else { - props[propName] = config[propName]; - } + props[propName] = config[propName]; } } } @@ -800,11 +726,6 @@ export function createElement(type, config, children) { } } - if (__DEV__ && !disableStringRefs) { - if (hasValidRef(config)) { - warnIfStringRefCannotBeAutoConverted(config, config.__self); - } - } if (hasValidKey(config)) { if (__DEV__) { checkKeyStringCoercion(config.key); @@ -825,11 +746,7 @@ export function createElement(type, config, children) { propName !== '__self' && propName !== '__source' ) { - if (!disableStringRefs && propName === 'ref') { - props.ref = coerceStringRef(config[propName], getOwner(), type); - } else { - props[propName] = config[propName]; - } + props[propName] = config[propName]; } } } @@ -889,7 +806,7 @@ export function cloneAndReplaceKey(oldElement, newKey) { newKey, undefined, undefined, - !__DEV__ && disableStringRefs ? undefined : oldElement._owner, + !__DEV__ ? undefined : oldElement._owner, oldElement.props, __DEV__ && enableOwnerStacks ? oldElement._debugStack : undefined, __DEV__ && enableOwnerStacks ? oldElement._debugTask : undefined, @@ -921,11 +838,11 @@ export function cloneElement(element, config, children) { let key = element.key; // Owner will be preserved, unless ref is overridden - let owner = !__DEV__ && disableStringRefs ? undefined : element._owner; + let owner = !__DEV__ ? undefined : element._owner; if (config != null) { if (hasValidRef(config)) { - owner = __DEV__ || !disableStringRefs ? getOwner() : undefined; + owner = __DEV__ ? getOwner() : undefined; } if (hasValidKey(config)) { if (__DEV__) { @@ -969,11 +886,7 @@ export function cloneElement(element, config, children) { // Resolve default props props[propName] = defaultProps[propName]; } else { - if (!disableStringRefs && propName === 'ref') { - props.ref = coerceStringRef(config[propName], owner, element.type); - } else { - props[propName] = config[propName]; - } + props[propName] = config[propName]; } } } @@ -1173,99 +1086,3 @@ function getCurrentComponentErrorInfo(parentType) { return info; } } - -function coerceStringRef(mixedRef, owner, type) { - if (disableStringRefs) { - return mixedRef; - } - - let stringRef; - if (typeof mixedRef === 'string') { - stringRef = mixedRef; - } else { - if (typeof mixedRef === 'number' || typeof mixedRef === 'boolean') { - if (__DEV__) { - checkPropStringCoercion(mixedRef, 'ref'); - } - stringRef = '' + mixedRef; - } else { - return mixedRef; - } - } - - const callback = stringRefAsCallbackRef.bind(null, stringRef, type, owner); - // This is used to check whether two callback refs conceptually represent - // the same string ref, and can therefore be reused by the reconciler. Needed - // for backwards compatibility with old Meta code that relies on string refs - // not being reattached on every render. - callback.__stringRef = stringRef; - callback.__type = type; - callback.__owner = owner; - return callback; -} - -function stringRefAsCallbackRef(stringRef, type, owner, value) { - if (disableStringRefs) { - return; - } - if (!owner) { - throw new Error( - `Element ref was specified as a string (${stringRef}) but no owner was set. This could happen for one of` + - ' the following reasons:\n' + - '1. You may be adding a ref to a function component\n' + - "2. You may be adding a ref to a component that was not created inside a component's render method\n" + - '3. You have multiple copies of React loaded\n' + - 'See https://react.dev/link/refs-must-have-owner for more information.', - ); - } - if (owner.tag !== ClassComponent) { - throw new Error( - 'Function components cannot have string refs. ' + - 'We recommend using useRef() instead. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-string-ref', - ); - } - - if (__DEV__) { - if ( - // Will already warn with "Function components cannot be given refs" - !(typeof type === 'function' && !isReactClass(type)) - ) { - const componentName = getComponentNameFromFiber(owner) || 'Component'; - if (!didWarnAboutStringRefs[componentName]) { - if (__DEV__) { - console.error( - 'Component "%s" contains the string ref "%s". Support for string refs ' + - 'will be removed in a future major release. We recommend using ' + - 'useRef() or createRef() instead. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-string-ref', - componentName, - stringRef, - ); - } - didWarnAboutStringRefs[componentName] = true; - } - } - } - - const inst = owner.stateNode; - if (!inst) { - throw new Error( - `Missing owner for string ref ${stringRef}. This error is likely caused by a ` + - 'bug in React. Please file an issue.', - ); - } - - const refs = inst.refs; - if (value === null) { - delete refs[stringRef]; - } else { - refs[stringRef] = value; - } -} - -function isReactClass(type) { - return type.prototype && type.prototype.isReactComponent; -} diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index d1159e60373c..44d6e8629db8 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -208,8 +208,6 @@ export const enableFilterEmptyStringAttributesDOM = true; // Disabled caching behavior of `react/cache` in client runtimes. export const disableClientCache = true; -export const disableStringRefs = true; - // Warn on any usage of ReactTestRenderer export const enableReactTestRendererWarning = true; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 7e63db5ef45e..a553c275fed8 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -41,7 +41,6 @@ export const disableLegacyContext = false; export const disableLegacyContextForFunctionComponents = false; export const disableLegacyMode = false; export const disableSchedulerTimeoutInWorkLoop = false; -export const disableStringRefs = true; export const disableTextareaChildren = false; export const enableAsyncActions = true; export const enableAsyncDebugInfo = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 644b23b46afa..d59e3f9698d4 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -30,7 +30,6 @@ export const disableLegacyContext = true; export const disableLegacyContextForFunctionComponents = true; export const disableLegacyMode = false; export const disableSchedulerTimeoutInWorkLoop = false; -export const disableStringRefs = true; export const disableTextareaChildren = false; export const enableAsyncActions = true; export const enableAsyncDebugInfo = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 2946d5efa11b..c27b2d6913dc 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -90,7 +90,6 @@ export const enableSiblingPrerendering = false; // We really need to get rid of this whole module. Any test renderer specific // flags should be handled by the Fiber config. // const __NEXT_MAJOR__ = __EXPERIMENTAL__; -export const disableStringRefs = true; export const disableLegacyMode = true; export const disableLegacyContext = true; export const disableLegacyContextForFunctionComponents = true; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index 1d505aaf56b9..170145c93db6 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -22,7 +22,6 @@ export const disableLegacyContext = false; export const disableLegacyContextForFunctionComponents = false; export const disableLegacyMode = false; export const disableSchedulerTimeoutInWorkLoop = false; -export const disableStringRefs = true; export const disableTextareaChildren = false; export const enableAsyncActions = true; export const enableAsyncDebugInfo = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 8e9e8f0ca2d5..46e939fd7319 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -82,8 +82,6 @@ export const disableClientCache = true; export const enableServerComponentLogs = true; export const enableInfiniteRenderLoopDetection = false; -export const disableStringRefs = true; - export const enableReactTestRendererWarning = false; export const disableLegacyMode = true; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index dac110129ef5..32a39121441b 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -52,7 +52,6 @@ export const enableSuspenseAvoidThisFallback = true; export const enableSuspenseAvoidThisFallbackFizz = false; export const disableIEWorkarounds = true; -export const disableStringRefs = true; export const enableCPUSuspense = true; export const enableUseMemoCacheHook = true; export const enableUseEffectEventHook = true;