@@ -1177,6 +1211,7 @@ describe('ReactDOMLegacyFiber', () => {
);
});
+ // @gate !disableLegacyMode
it('should not warn when rendering into an empty container', () => {
ReactDOM.render(
');
@@ -1186,6 +1221,7 @@ describe('ReactDOMLegacyFiber', () => {
expect(container.innerHTML).toBe('
');
});
+ // @gate !disableLegacyMode
it('should warn when replacing a container which was manually updated outside of React', () => {
// when not messing with the DOM outside of React
ReactDOM.render(
, container);
@@ -1210,6 +1246,7 @@ describe('ReactDOMLegacyFiber', () => {
}).toThrowError();
});
+ // @gate !disableLegacyMode
it('should warn when doing an update to a container manually updated outside of React', () => {
// when not messing with the DOM outside of React
ReactDOM.render(
, container);
@@ -1227,6 +1264,7 @@ describe('ReactDOMLegacyFiber', () => {
);
});
+ // @gate !disableLegacyMode
it('should warn when doing an update to a container manually cleared outside of React', () => {
// when not messing with the DOM outside of React
ReactDOM.render(
, container);
@@ -1244,6 +1282,7 @@ describe('ReactDOMLegacyFiber', () => {
);
});
+ // @gate !disableLegacyMode
it('should render a text component with a text DOM node on the same document as the container', () => {
// 1. Create a new document through the use of iframe
// 2. Set up the spy to make asserts when a text component
@@ -1274,6 +1313,7 @@ describe('ReactDOMLegacyFiber', () => {
expect(iframeContainer.appendChild).toHaveBeenCalledTimes(1);
});
+ // @gate !disableLegacyMode
it('should mount into a document fragment', () => {
const fragment = document.createDocumentFragment();
ReactDOM.render(
, fragment);
@@ -1283,6 +1323,7 @@ describe('ReactDOMLegacyFiber', () => {
});
// Regression test for https://github.com/facebook/react/issues/12643#issuecomment-413727104
+ // @gate !disableLegacyMode
it('should not diff memoized host components', () => {
const inputRef = React.createRef();
let didCallOnChange = false;
@@ -1341,6 +1382,7 @@ describe('ReactDOMLegacyFiber', () => {
expect(didCallOnChange).toBe(true);
});
+ // @gate !disableLegacyMode
it('unmounted legacy roots should never clear newer root content from a container', () => {
const ref = React.createRef();
diff --git a/packages/react-dom/src/__tests__/ReactDOMOption-test.js b/packages/react-dom/src/__tests__/ReactDOMOption-test.js
index a75ce875001f6..873aad71d21d3 100644
--- a/packages/react-dom/src/__tests__/ReactDOMOption-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMOption-test.js
@@ -13,7 +13,6 @@ describe('ReactDOMOption', () => {
let React;
let ReactDOMClient;
let ReactDOMServer;
- let ReactTestUtils;
let act;
beforeEach(() => {
@@ -21,41 +20,47 @@ describe('ReactDOMOption', () => {
React = require('react');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
- ReactTestUtils = require('react-dom/test-utils');
act = require('internal-test-utils').act;
});
- it('should flatten children to a string', () => {
+ async function renderIntoDocument(children) {
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ await act(async () => root.render(children));
+ return container;
+ }
+
+ it('should flatten children to a string', async () => {
const stub = (
);
- const node = ReactTestUtils.renderIntoDocument(stub);
+ const container = await renderIntoDocument(stub);
- expect(node.innerHTML).toBe('1 foo');
+ expect(container.firstChild.innerHTML).toBe('1 foo');
});
- it('should warn for invalid child tags', () => {
+ it('should warn for invalid child tags', async () => {
const el = (
);
- let node;
- expect(() => {
- node = ReactTestUtils.renderIntoDocument(el);
+ let container;
+ await expect(async () => {
+ container = await renderIntoDocument(el);
}).toErrorDev(
'In HTML,
cannot be a child of
);
- let node;
- expect(() => {
- node = ReactTestUtils.renderIntoDocument(el);
+ let container;
+ await expect(async () => {
+ container = await renderIntoDocument(el);
}).toErrorDev(
'Cannot infer the option value of complex children. ' +
'Pass a `value` prop or use a plain string as children to
);
- const node = ReactTestUtils.renderIntoDocument(el);
- expect(node.innerHTML).toBe('1 2 3');
- ReactTestUtils.renderIntoDocument(el);
+ const container = await renderIntoDocument(el);
+ expect(container.firstChild.innerHTML).toBe('1 2 3');
+ await renderIntoDocument(el);
});
- it('should ignore null/undefined/false children without warning', () => {
+ it('should ignore null/undefined/false children without warning', async () => {
const stub = (
);
- const node = ReactTestUtils.renderIntoDocument(stub);
+ const container = await renderIntoDocument(stub);
- expect(node.innerHTML).toBe('1 2');
+ expect(container.firstChild.innerHTML).toBe('1 2');
});
- it('should throw on object children', () => {
- expect(() => {
- ReactTestUtils.renderIntoDocument(
);
- }).toThrow('Objects are not valid as a React child');
- expect(() => {
- ReactTestUtils.renderIntoDocument(
);
- }).toThrow('Objects are not valid as a React child');
- expect(() => {
- ReactTestUtils.renderIntoDocument(
+ it('should throw on object children', async () => {
+ await expect(async () =>
+ renderIntoDocument(
),
+ ).rejects.toThrow('Objects are not valid as a React child');
+ await expect(async () => {
+ await renderIntoDocument(
);
+ }).rejects.toThrow('Objects are not valid as a React child');
+ await expect(async () => {
+ await renderIntoDocument(
,
);
- }).toThrow('Objects are not valid as a React child');
- expect(() => {
- ReactTestUtils.renderIntoDocument(
+ }).rejects.toThrow('Objects are not valid as a React child');
+ await expect(async () => {
+ await renderIntoDocument(
,
);
- }).toThrow('Objects are not valid as a React child');
+ }).rejects.toThrow('Objects are not valid as a React child');
});
- it('should support element-ish child', () => {
+ it('should support element-ish child', async () => {
// This is similar to
.
// We don't toString it because you must instead provide a value prop.
const obj = {
@@ -145,51 +150,45 @@ describe('ReactDOMOption', () => {
},
};
- let node = ReactTestUtils.renderIntoDocument(
- ,
- );
- expect(node.innerHTML).toBe('hello');
+ let container = await renderIntoDocument();
+ expect(container.firstChild.innerHTML).toBe('hello');
- node = ReactTestUtils.renderIntoDocument(
- ,
- );
- expect(node.innerHTML).toBe('hello');
+ container = await renderIntoDocument();
+ expect(container.firstChild.innerHTML).toBe('hello');
- node = ReactTestUtils.renderIntoDocument(
- ,
- );
- expect(node.innerHTML).toBe('hello');
- expect(node.value).toBe('hello');
+ container = await renderIntoDocument();
+ expect(container.firstChild.innerHTML).toBe('hello');
+ expect(container.firstChild.value).toBe('hello');
- node = ReactTestUtils.renderIntoDocument(
+ container = await renderIntoDocument(
,
);
- expect(node.innerHTML).toBe('1hello2');
- expect(node.value).toBe('hello');
+ expect(container.firstChild.innerHTML).toBe('1hello2');
+ expect(container.firstChild.value).toBe('hello');
});
// @gate enableBigIntSupport
- it('should support bigint values', () => {
- const node = ReactTestUtils.renderIntoDocument();
- expect(node.innerHTML).toBe('5');
- expect(node.value).toBe('5');
+ it('should support bigint values', async () => {
+ const container = await renderIntoDocument();
+ expect(container.firstChild.innerHTML).toBe('5');
+ expect(container.firstChild.value).toBe('5');
});
- it('should be able to use dangerouslySetInnerHTML on option', () => {
+ it('should be able to use dangerouslySetInnerHTML on option', async () => {
const stub = ;
- let node;
- expect(() => {
- node = ReactTestUtils.renderIntoDocument(stub);
+ let container;
+ await expect(async () => {
+ container = await renderIntoDocument(stub);
}).toErrorDev(
'Pass a `value` prop if you set dangerouslyInnerHTML so React knows which value should be selected.\n' +
' in option (at **)',
);
- expect(node.innerHTML).toBe('foobar');
+ expect(container.firstChild.innerHTML).toBe('foobar');
});
it('should set attribute for empty value', async () => {
diff --git a/packages/react-dom/src/__tests__/ReactDOMSelect-test.js b/packages/react-dom/src/__tests__/ReactDOMSelect-test.js
index 35aaba29a6a46..849e179920ac7 100644
--- a/packages/react-dom/src/__tests__/ReactDOMSelect-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMSelect-test.js
@@ -965,6 +965,7 @@ describe('ReactDOMSelect', () => {
expect(node.options[2].selected).toBe(false); // c
});
+ // @gate !disableLegacyMode
it('should allow controlling `value` in a nested legacy render', async () => {
let selectNode;
diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationReconnecting-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationReconnecting-test.js
index 1cbc61dfd0116..3eae27486a097 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationReconnecting-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationReconnecting-test.js
@@ -477,6 +477,7 @@ describe('ReactDOMServerIntegration (legacy)', () => {
resetModules();
});
+ // @gate !disableLegacyMode
it('legacy mode can explicitly ignore errors reconnecting different element types of children', () =>
expectMarkupMatch(
@@ -487,6 +488,7 @@ describe('ReactDOMServerIntegration (legacy)', () => {
,
));
+ // @gate !disableLegacyMode
it('legacy mode can explicitly ignore reconnecting more children', () =>
expectMarkupMatch(
@@ -498,6 +500,7 @@ describe('ReactDOMServerIntegration (legacy)', () => {
,
));
+ // @gate !disableLegacyMode
it('legacy mode can explicitly ignore reconnecting fewer children', () =>
expectMarkupMatch(
@@ -509,6 +512,7 @@ describe('ReactDOMServerIntegration (legacy)', () => {
,
));
+ // @gate !disableLegacyMode
it('legacy mode can explicitly ignore reconnecting reordered children', () =>
expectMarkupMatch(
diff --git a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
index 8d0023b6f2078..5b10d81f2220d 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
@@ -718,6 +718,7 @@ describe('ReactDOMServerPartialHydration', () => {
expect(deleted.length).toBe(1);
});
+ // @gate !disableLegacyMode
it('warns and replaces the boundary content in legacy mode', async () => {
let suspend = false;
let resolve;
diff --git a/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js b/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js
index ff1cbf066322d..307f848a16cce 100644
--- a/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMSingletonComponents-test.js
@@ -996,16 +996,19 @@ describe('ReactDOM HostSingleton', () => {
});
// https://github.com/facebook/react/issues/26128
+ // @gate !disableLegacyMode
it('(#26128) does not throw when rendering at body in legacy mode', async () => {
ReactDOM.render(
, document.body);
});
// https://github.com/facebook/react/issues/26128
+ // @gate !disableLegacyMode
it('(#26128) does not throw when rendering at in legacy mode', async () => {
ReactDOM.render(
, document.documentElement);
});
// https://github.com/facebook/react/issues/26128
+ // @gate !disableLegacyMode
it('(#26128) does not throw when rendering at document in legacy mode', async () => {
ReactDOM.render(, document);
});
diff --git a/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js b/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js
index 537e448f86dd8..089e6329e068b 100644
--- a/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js
@@ -100,6 +100,7 @@ describe('ReactDOMSuspensePlaceholder', () => {
return text;
}
+ // @gate !disableLegacyMode
it('hides and unhides timed out DOM elements in legacy roots', async () => {
const divs = [
React.createRef(null),
@@ -160,6 +161,7 @@ describe('ReactDOMSuspensePlaceholder', () => {
expect(container.textContent).toEqual('ABC');
});
+ // @gate !disableLegacyMode
it(
'in legacy roots, re-hides children if their display is updated ' +
'but the boundary is still showing the fallback',
@@ -213,6 +215,7 @@ describe('ReactDOMSuspensePlaceholder', () => {
);
// Regression test for https://github.com/facebook/react/issues/14188
+ // @gate !disableLegacyMode
it('can call findDOMNode() in a suspended component commit phase in legacy roots', async () => {
const log = [];
const Lazy = React.lazy(
diff --git a/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js
index 42908a693cce3..974403acc4197 100644
--- a/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js
@@ -25,6 +25,7 @@ describe('ReactLegacyCompositeComponent', () => {
act = require('internal-test-utils').act;
});
+ // @gate !disableLegacyMode
it('should warn about `setState` in render in legacy mode', () => {
const container = document.createElement('div');
@@ -377,6 +378,7 @@ describe('ReactLegacyCompositeComponent', () => {
});
// @gate !disableLegacyContext
+ // @gate !disableLegacyMode
it('unmasked context propagates through updates', () => {
class Leaf extends React.Component {
static contextTypes = {
@@ -441,6 +443,7 @@ describe('ReactLegacyCompositeComponent', () => {
});
// @gate !disableLegacyContext
+ // @gate !disableLegacyMode
it('should trigger componentWillReceiveProps for context changes', () => {
let contextChanges = 0;
let propChanges = 0;
@@ -553,6 +556,7 @@ describe('ReactLegacyCompositeComponent', () => {
expect(contextChanges).toBe(3); // ChildWithContext, GrandChild x 2
});
+ // @gate !disableLegacyMode
it('only renders once if updated in componentWillReceiveProps in legacy mode', () => {
let renders = 0;
@@ -581,6 +585,7 @@ describe('ReactLegacyCompositeComponent', () => {
expect(instance.state.updated).toBe(true);
});
+ // @gate !disableLegacyMode
it('only renders once if updated in componentWillReceiveProps when batching in legacy mode', () => {
let renders = 0;
@@ -611,6 +616,7 @@ describe('ReactLegacyCompositeComponent', () => {
expect(instance.state.updated).toBe(true);
});
+ // @gate !disableLegacyMode
it('should update refs if shouldComponentUpdate gives false in legacy mode', () => {
class Static extends React.Component {
shouldComponentUpdate() {
@@ -665,6 +671,7 @@ describe('ReactLegacyCompositeComponent', () => {
expect(ReactDOM.findDOMNode(comp.static1Ref.current).textContent).toBe('A');
});
+ // @gate !disableLegacyMode
it('should allow access to findDOMNode in componentWillUnmount in legacy mode', () => {
let a = null;
let b = null;
@@ -693,6 +700,7 @@ describe('ReactLegacyCompositeComponent', () => {
});
// @gate !disableLegacyContext || !__DEV__
+ // @gate !disableLegacyMode
it('context should be passed down from the parent', () => {
class Parent extends React.Component {
static childContextTypes = {
@@ -813,6 +821,7 @@ describe('ReactLegacyCompositeComponent', () => {
expect(moo.state.amIImmutable).toBe(undefined);
});
+ // @gate !disableLegacyMode
it('should not warn about unmounting during unmounting in legacy mode', () => {
const container = document.createElement('div');
const layer = document.createElement('div');
diff --git a/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js b/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
index bcdf2b796134e..d16d6253e36d1 100644
--- a/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
@@ -588,6 +588,7 @@ describe('ReactLegacyErrorBoundaries', () => {
jest.restoreAllMocks();
});
+ // @gate !disableLegacyMode
it('does not swallow exceptions on mounting without boundaries', () => {
let container = document.createElement('div');
expect(() => {
@@ -605,6 +606,7 @@ describe('ReactLegacyErrorBoundaries', () => {
}).toThrow('Hello');
});
+ // @gate !disableLegacyMode
it('does not swallow exceptions on updating without boundaries', () => {
let container = document.createElement('div');
ReactDOM.render(
, container);
@@ -625,6 +627,7 @@ describe('ReactLegacyErrorBoundaries', () => {
}).toThrow('Hello');
});
+ // @gate !disableLegacyMode
it('does not swallow exceptions on unmounting without boundaries', () => {
const container = document.createElement('div');
ReactDOM.render(
, container);
@@ -633,6 +636,7 @@ describe('ReactLegacyErrorBoundaries', () => {
}).toThrow('Hello');
});
+ // @gate !disableLegacyMode
it('prevents errors from leaking into other roots', () => {
const container1 = document.createElement('div');
const container2 = document.createElement('div');
@@ -670,6 +674,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(container3.firstChild).toBe(null);
});
+ // @gate !disableLegacyMode
it('logs a single error using both error boundaries', () => {
const container = document.createElement('div');
spyOnDev(console, 'error');
@@ -715,6 +720,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['BothErrorBoundaries componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('renders an error state if child throws in render', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -745,6 +751,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('renders an error state if child throws in constructor', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -773,6 +780,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('renders an error state if child throws in componentWillMount', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -801,6 +809,7 @@ describe('ReactLegacyErrorBoundaries', () => {
});
// @gate !disableLegacyContext || !__DEV__
+ // @gate !disableLegacyMode
it('renders an error state if context provider throws in componentWillMount', () => {
class BrokenComponentWillMountWithContext extends React.Component {
static childContextTypes = {foo: PropTypes.number};
@@ -826,6 +835,7 @@ describe('ReactLegacyErrorBoundaries', () => {
});
if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
+ // @gate !disableLegacyMode
it('renders an error state if module-style context provider throws in componentWillMount', () => {
function BrokenComponentWillMountWithContext() {
return {
@@ -864,6 +874,7 @@ describe('ReactLegacyErrorBoundaries', () => {
});
}
+ // @gate !disableLegacyMode
it('mounts the error message if mounting fails', () => {
function renderError(error) {
return
;
@@ -902,6 +913,7 @@ describe('ReactLegacyErrorBoundaries', () => {
]);
});
+ // @gate !disableLegacyMode
it('propagates errors on retry on mounting', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -946,6 +958,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('propagates errors inside boundary during componentWillMount', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -974,6 +987,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('propagates errors inside boundary while rendering error state', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1017,6 +1031,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('does not call componentWillUnmount when aborting initial mount', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1056,6 +1071,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('resets callback refs if mounting aborts', () => {
function childRef(x) {
log.push('Child ref is set to ' + x);
@@ -1100,6 +1116,7 @@ describe('ReactLegacyErrorBoundaries', () => {
]);
});
+ // @gate !disableLegacyMode
it('resets object refs if mounting aborts', () => {
const childRef = React.createRef();
const errorMessageRef = React.createRef();
@@ -1140,6 +1157,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(errorMessageRef.current).toEqual(null);
});
+ // @gate !disableLegacyMode
it('successfully mounts if no error occurs', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1161,6 +1179,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('catches if child throws in constructor during update', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1209,6 +1228,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('catches if child throws in componentWillMount during update', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1258,6 +1278,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('catches if child throws in componentWillReceiveProps during update', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1302,6 +1323,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('catches if child throws in componentWillUpdate during update', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1347,6 +1369,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('catches if child throws in render during update', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1396,6 +1419,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('keeps refs up-to-date during updates', () => {
function child1Ref(x) {
log.push('Child1 ref is set to ' + x);
@@ -1460,6 +1484,7 @@ describe('ReactLegacyErrorBoundaries', () => {
]);
});
+ // @gate !disableLegacyMode
it('recovers from componentWillUnmount errors on update', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1516,6 +1541,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('recovers from nested componentWillUnmount errors on update', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1577,6 +1603,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('picks the right boundary when handling unmounting errors', () => {
function renderInnerError(error) {
return
Caught an inner error: {error.message}.
;
@@ -1646,6 +1673,7 @@ describe('ReactLegacyErrorBoundaries', () => {
]);
});
+ // @gate !disableLegacyMode
it('can recover from error state', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1694,6 +1722,7 @@ describe('ReactLegacyErrorBoundaries', () => {
]);
});
+ // @gate !disableLegacyMode
it('can update multiple times in error state', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1718,6 +1747,7 @@ describe('ReactLegacyErrorBoundaries', () => {
ReactDOM.unmountComponentAtNode(container);
});
+ // @gate !disableLegacyMode
it("doesn't get into inconsistent state during removals", () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1737,6 +1767,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it("doesn't get into inconsistent state during additions", () => {
const container = document.createElement('div');
ReactDOM.render(
, container);
@@ -1755,6 +1786,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it("doesn't get into inconsistent state during reorders", () => {
function getAMixOfNormalAndBrokenRenderElements() {
const elements = [];
@@ -1803,6 +1835,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('catches errors originating downstream', () => {
let fail = false;
class Stateful extends React.Component {
@@ -1845,6 +1878,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('catches errors in componentDidMount', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1904,6 +1938,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('catches errors in componentDidUpdate', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1943,6 +1978,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('propagates errors inside boundary during componentDidMount', () => {
const container = document.createElement('div');
ReactDOM.render(
@@ -1980,6 +2016,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('calls componentDidCatch for each error that is captured', () => {
function renderUnmountError(error) {
return
Caught an unmounting error: {error.message}.
;
@@ -2081,6 +2118,7 @@ describe('ReactLegacyErrorBoundaries', () => {
]);
});
+ // @gate !disableLegacyMode
it('discards a bad root if the root component fails', () => {
const X = null;
const Y = undefined;
@@ -2112,6 +2150,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(err2.message).toMatch(/got: undefined/);
});
+ // @gate !disableLegacyMode
it('renders empty output if error boundary does not handle the error', () => {
const container = document.createElement('div');
expect(() => {
@@ -2146,6 +2185,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(log).toEqual(['NoopErrorBoundary componentWillUnmount']);
});
+ // @gate !disableLegacyMode
it('passes first error when two errors happen in commit', () => {
const errors = [];
let caughtError;
@@ -2185,6 +2225,7 @@ describe('ReactLegacyErrorBoundaries', () => {
expect(caughtError.message).toBe('child sad');
});
+ // @gate !disableLegacyMode
it('propagates uncaught error inside unbatched initial mount', () => {
function Foo() {
throw new Error('foo error');
@@ -2197,6 +2238,7 @@ describe('ReactLegacyErrorBoundaries', () => {
}).toThrow('foo error');
});
+ // @gate !disableLegacyMode
it('handles errors that occur in before-mutation commit hook', () => {
const errors = [];
let caughtError;
diff --git a/packages/react-dom/src/__tests__/ReactLegacyMount-test.js b/packages/react-dom/src/__tests__/ReactLegacyMount-test.js
index 8bf1d3e2f0022..ee07bb998bdf6 100644
--- a/packages/react-dom/src/__tests__/ReactLegacyMount-test.js
+++ b/packages/react-dom/src/__tests__/ReactLegacyMount-test.js
@@ -37,6 +37,7 @@ describe('ReactMount', () => {
});
describe('unmountComponentAtNode', () => {
+ // @gate !disableLegacyMode
it('throws when given a non-node', () => {
const nodeArray = document.getElementsByTagName('div');
expect(() => {
@@ -44,6 +45,7 @@ describe('ReactMount', () => {
}).toThrowError('Target container is not a DOM element.');
});
+ // @gate !disableLegacyMode
it('returns false on non-React containers', () => {
const d = document.createElement('div');
d.innerHTML = '
hellooo';
@@ -51,6 +53,7 @@ describe('ReactMount', () => {
expect(d.textContent).toBe('hellooo');
});
+ // @gate !disableLegacyMode
it('returns true on React containers', () => {
const d = document.createElement('div');
ReactDOM.render(
hellooo, d);
@@ -60,6 +63,7 @@ describe('ReactMount', () => {
});
});
+ // @gate !disableLegacyMode
it('warns when given a factory', () => {
class Component extends React.Component {
render() {
@@ -76,6 +80,7 @@ describe('ReactMount', () => {
);
});
+ // @gate !disableLegacyMode
it('should render different components in same root', () => {
const container = document.createElement('container');
document.body.appendChild(container);
@@ -87,6 +92,7 @@ describe('ReactMount', () => {
expect(container.firstChild.nodeName).toBe('SPAN');
});
+ // @gate !disableLegacyMode
it('should unmount and remount if the key changes', () => {
const container = document.createElement('container');
@@ -122,6 +128,7 @@ describe('ReactMount', () => {
expect(mockUnmount).toHaveBeenCalledTimes(1);
});
+ // @gate !disableLegacyMode
it('should reuse markup if rendering to the same target twice', () => {
const container = document.createElement('container');
const instance1 = ReactDOM.render(
, container);
@@ -130,6 +137,7 @@ describe('ReactMount', () => {
expect(instance1 === instance2).toBe(true);
});
+ // @gate !disableLegacyMode
it('does not warn if mounting into left padded rendered markup', () => {
const container = document.createElement('container');
container.innerHTML = ReactDOMServer.renderToString(
) + ' ';
@@ -138,6 +146,7 @@ describe('ReactMount', () => {
ReactDOM.hydrate(
, container);
});
+ // @gate !disableLegacyMode
it('should warn if mounting into right padded rendered markup', () => {
const container = document.createElement('container');
container.innerHTML = ' ' + ReactDOMServer.renderToString(
);
@@ -147,6 +156,7 @@ describe('ReactMount', () => {
);
});
+ // @gate !disableLegacyMode
it('should not warn if mounting into non-empty node', () => {
const container = document.createElement('container');
container.innerHTML = '
';
@@ -154,6 +164,7 @@ describe('ReactMount', () => {
ReactDOM.render(
, container);
});
+ // @gate !disableLegacyMode
it('should warn when mounting into document.body', () => {
const iFrame = document.createElement('iframe');
document.body.appendChild(iFrame);
@@ -162,6 +173,7 @@ describe('ReactMount', () => {
ReactDOM.render(
, iFrame.contentDocument.body);
});
+ // @gate !disableLegacyMode
it('should account for escaping on a checksum mismatch', () => {
const div = document.createElement('div');
const markup = ReactDOMServer.renderToString(
@@ -180,6 +192,7 @@ describe('ReactMount', () => {
);
});
+ // @gate !disableLegacyMode
it('should warn if render removes React-rendered children', () => {
const container = document.createElement('container');
@@ -207,6 +220,7 @@ describe('ReactMount', () => {
);
});
+ // @gate !disableLegacyMode
it('should warn if the unmounted node was rendered by another copy of React', () => {
jest.resetModules();
const ReactDOMOther = require('react-dom');
@@ -236,6 +250,7 @@ describe('ReactMount', () => {
ReactDOM.unmountComponentAtNode(container);
});
+ // @gate !disableLegacyMode
it('passes the correct callback context', () => {
const container = document.createElement('div');
let calls = 0;
@@ -276,6 +291,7 @@ describe('ReactMount', () => {
expect(calls).toBe(5);
});
+ // @gate !disableLegacyMode
it('initial mount of legacy root is sync inside batchedUpdates, as if it were wrapped in flushSync', () => {
const container1 = document.createElement('div');
const container2 = document.createElement('div');
@@ -322,6 +338,7 @@ describe('ReactMount', () => {
expect(mountPoint.nodeType).toBe(COMMENT_NODE);
});
+ // @gate !disableLegacyMode
it('renders at a comment node', () => {
function Char(props) {
return props.children;
@@ -347,6 +364,7 @@ describe('ReactMount', () => {
});
});
+ // @gate !disableLegacyMode
it('clears existing children with legacy API', async () => {
const container = document.createElement('div');
container.innerHTML = '
a
b
';
@@ -369,6 +387,7 @@ describe('ReactMount', () => {
expect(container.textContent).toEqual('dc');
});
+ // @gate !disableLegacyMode
it('warns when rendering with legacy API into createRoot() container', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
@@ -393,6 +412,7 @@ describe('ReactMount', () => {
expect(container.textContent).toEqual('Bye');
});
+ // @gate !disableLegacyMode
it('callback passed to legacy hydrate() API', () => {
const container = document.createElement('div');
container.innerHTML = '
Hi
';
@@ -403,6 +423,7 @@ describe('ReactMount', () => {
assertLog(['callback']);
});
+ // @gate !disableLegacyMode
it('warns when unmounting with legacy API (no previous content)', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
@@ -430,6 +451,7 @@ describe('ReactMount', () => {
expect(container.textContent).toEqual('');
});
+ // @gate !disableLegacyMode
it('warns when unmounting with legacy API (has previous content)', async () => {
const container = document.createElement('div');
// Currently createRoot().render() doesn't clear this.
@@ -458,6 +480,7 @@ describe('ReactMount', () => {
expect(container.textContent).toEqual('');
});
+ // @gate !disableLegacyMode
it('warns when passing legacy container to createRoot()', () => {
const container = document.createElement('div');
ReactDOM.render(
Hi
, container);
diff --git a/packages/react-dom/src/__tests__/ReactLegacyRootWarnings-test.js b/packages/react-dom/src/__tests__/ReactLegacyRootWarnings-test.js
index 9504de2c0a97e..c686a67d43d15 100644
--- a/packages/react-dom/src/__tests__/ReactLegacyRootWarnings-test.js
+++ b/packages/react-dom/src/__tests__/ReactLegacyRootWarnings-test.js
@@ -13,6 +13,7 @@ describe('ReactDOMRoot', () => {
jest.restoreAllMocks();
});
+ // @gate !disableLegacyMode
test('deprecation warning for ReactDOM.render', () => {
spyOnDev(console, 'error');
@@ -26,6 +27,7 @@ describe('ReactDOMRoot', () => {
}
});
+ // @gate !disableLegacyMode
test('deprecation warning for ReactDOM.hydrate', () => {
spyOnDev(console, 'error');
diff --git a/packages/react-dom/src/__tests__/ReactLegacyUpdates-test.js b/packages/react-dom/src/__tests__/ReactLegacyUpdates-test.js
index 0e92ce3490b93..5cba05a83e35a 100644
--- a/packages/react-dom/src/__tests__/ReactLegacyUpdates-test.js
+++ b/packages/react-dom/src/__tests__/ReactLegacyUpdates-test.js
@@ -31,6 +31,7 @@ describe('ReactLegacyUpdates', () => {
assertLog = InternalTestUtils.assertLog;
});
+ // @gate !disableLegacyMode
it('should batch state when updating state twice', () => {
let updateCount = 0;
@@ -60,6 +61,7 @@ describe('ReactLegacyUpdates', () => {
expect(updateCount).toBe(1);
});
+ // @gate !disableLegacyMode
it('should batch state when updating two different state keys', () => {
let updateCount = 0;
@@ -92,6 +94,7 @@ describe('ReactLegacyUpdates', () => {
expect(updateCount).toBe(1);
});
+ // @gate !disableLegacyMode
it('should batch state and props together', () => {
let updateCount = 0;
@@ -125,6 +128,7 @@ describe('ReactLegacyUpdates', () => {
expect(updateCount).toBe(1);
});
+ // @gate !disableLegacyMode
it('should batch parent/child state updates together', () => {
let parentUpdateCount = 0;
@@ -179,6 +183,7 @@ describe('ReactLegacyUpdates', () => {
expect(childUpdateCount).toBe(1);
});
+ // @gate !disableLegacyMode
it('should batch child/parent state updates together', () => {
let parentUpdateCount = 0;
@@ -235,6 +240,7 @@ describe('ReactLegacyUpdates', () => {
expect(childUpdateCount).toBe(1);
});
+ // @gate !disableLegacyMode
it('should support chained state updates', () => {
let updateCount = 0;
@@ -274,6 +280,7 @@ describe('ReactLegacyUpdates', () => {
expect(updateCount).toBe(2);
});
+ // @gate !disableLegacyMode
it('should batch forceUpdate together', () => {
let shouldUpdateCount = 0;
let updateCount = 0;
@@ -316,6 +323,7 @@ describe('ReactLegacyUpdates', () => {
expect(updateCount).toBe(1);
});
+ // @gate !disableLegacyMode
it('should update children even if parent blocks updates', () => {
let parentRenderCount = 0;
let childRenderCount = 0;
@@ -364,6 +372,7 @@ describe('ReactLegacyUpdates', () => {
expect(childRenderCount).toBe(2);
});
+ // @gate !disableLegacyMode
it('should not reconcile children passed via props', () => {
let numMiddleRenders = 0;
let numBottomRenders = 0;
@@ -401,6 +410,7 @@ describe('ReactLegacyUpdates', () => {
expect(numBottomRenders).toBe(1);
});
+ // @gate !disableLegacyMode
it('should flow updates correctly', () => {
let willUpdates = [];
let didUpdates = [];
@@ -530,6 +540,7 @@ describe('ReactLegacyUpdates', () => {
);
});
+ // @gate !disableLegacyMode
it('should queue mount-ready handlers across different roots', () => {
// We'll define two components A and B, then update both of them. When A's
// componentDidUpdate handlers is called, B's DOM should already have been
@@ -579,6 +590,7 @@ describe('ReactLegacyUpdates', () => {
expect(aUpdated).toBe(true);
});
+ // @gate !disableLegacyMode
it('should flush updates in the correct order', () => {
const updates = [];
@@ -661,6 +673,7 @@ describe('ReactLegacyUpdates', () => {
/* eslint-enable indent */
});
+ // @gate !disableLegacyMode
it('should flush updates in the correct order across roots', () => {
const instances = [];
const updates = [];
@@ -699,6 +712,7 @@ describe('ReactLegacyUpdates', () => {
expect(updates).toEqual([0, 1, 2, 0, 1, 2]);
});
+ // @gate !disableLegacyMode
it('should queue nested updates', () => {
// See https://github.com/facebook/react/issues/1147
@@ -752,6 +766,7 @@ describe('ReactLegacyUpdates', () => {
expect(ReactDOM.findDOMNode(x).textContent).toBe('1');
});
+ // @gate !disableLegacyMode
it('should queue updates from during mount', () => {
// See https://github.com/facebook/react/issues/1353
let a;
@@ -791,6 +806,7 @@ describe('ReactLegacyUpdates', () => {
expect(ReactDOM.findDOMNode(a).textContent).toBe('A1');
});
+ // @gate !disableLegacyMode
it('calls componentWillReceiveProps setState callback properly', () => {
let callbackCount = 0;
@@ -817,6 +833,7 @@ describe('ReactLegacyUpdates', () => {
expect(callbackCount).toBe(1);
});
+ // @gate !disableLegacyMode
it('does not call render after a component as been deleted', () => {
let renderCount = 0;
let componentB = null;
@@ -854,6 +871,7 @@ describe('ReactLegacyUpdates', () => {
expect(renderCount).toBe(1);
});
+ // @gate !disableLegacyMode
it('throws in setState if the update callback is not a function', () => {
function Foo() {
this.a = 1;
@@ -897,6 +915,7 @@ describe('ReactLegacyUpdates', () => {
);
});
+ // @gate !disableLegacyMode
it('throws in forceUpdate if the update callback is not a function', () => {
function Foo() {
this.a = 1;
@@ -940,6 +959,7 @@ describe('ReactLegacyUpdates', () => {
);
});
+ // @gate !disableLegacyMode
it('does not update one component twice in a batch (#2410)', () => {
class Parent extends React.Component {
childRef = React.createRef();
@@ -992,6 +1012,7 @@ describe('ReactLegacyUpdates', () => {
});
});
+ // @gate !disableLegacyMode
it('does not update one component twice in a batch (#6371)', () => {
let callbacks = [];
function emitChange() {
@@ -1049,6 +1070,7 @@ describe('ReactLegacyUpdates', () => {
expect(result).toEqual(42);
});
+ // @gate !disableLegacyMode
it('unmounts and remounts a root in the same batch', () => {
const container = document.createElement('div');
ReactDOM.render(
a, container);
@@ -1059,6 +1081,7 @@ describe('ReactLegacyUpdates', () => {
expect(container.textContent).toBe('b');
});
+ // @gate !disableLegacyMode
it('handles reentrant mounting in synchronous mode', () => {
let mounts = 0;
class Editor extends React.Component {
@@ -1096,6 +1119,7 @@ describe('ReactLegacyUpdates', () => {
expect(mounts).toBe(1);
});
+ // @gate !disableLegacyMode
it('mounts and unmounts are sync even in a batch', () => {
const ops = [];
const container = document.createElement('div');
@@ -1108,6 +1132,7 @@ describe('ReactLegacyUpdates', () => {
expect(ops).toEqual(['Hello', '']);
});
+ // @gate !disableLegacyMode
it(
'in legacy mode, updates in componentWillUpdate and componentDidUpdate ' +
'should both flush in the immediately subsequent commit',
@@ -1151,6 +1176,7 @@ describe('ReactLegacyUpdates', () => {
},
);
+ // @gate !disableLegacyMode
it(
'in legacy mode, updates in componentWillUpdate and componentDidUpdate ' +
'(on a sibling) should both flush in the immediately subsequent commit',
@@ -1222,6 +1248,7 @@ describe('ReactLegacyUpdates', () => {
},
);
+ // @gate !disableLegacyMode
it('uses correct base state for setState inside render phase', () => {
const ops = [];
@@ -1245,6 +1272,7 @@ describe('ReactLegacyUpdates', () => {
expect(ops).toEqual(['base: 0, memoized: 0', 'base: 1, memoized: 1']);
});
+ // @gate !disableLegacyMode
it('does not re-render if state update is null', () => {
const container = document.createElement('div');
@@ -1265,6 +1293,7 @@ describe('ReactLegacyUpdates', () => {
});
// Will change once we switch to async by default
+ // @gate !disableLegacyMode
it('synchronously renders hidden subtrees', () => {
const container = document.createElement('div');
let ops = [];
@@ -1301,6 +1330,7 @@ describe('ReactLegacyUpdates', () => {
expect(ops).toEqual(['Foo', 'Bar', 'Baz']);
});
+ // @gate !disableLegacyMode
it('can render ridiculously large number of roots without triggering infinite update loop error', () => {
class Foo extends React.Component {
componentDidMount() {
@@ -1325,6 +1355,7 @@ describe('ReactLegacyUpdates', () => {
ReactDOM.render(
, container);
});
+ // @gate !disableLegacyMode
it('resets the update counter for unrelated updates', () => {
const container = document.createElement('div');
const ref = React.createRef();
@@ -1365,6 +1396,7 @@ describe('ReactLegacyUpdates', () => {
expect(ref.current).toBe(null);
});
+ // @gate !disableLegacyMode
it('does not fall into an infinite update loop', () => {
class NonTerminating extends React.Component {
state = {step: 0};
@@ -1390,6 +1422,7 @@ describe('ReactLegacyUpdates', () => {
}).toThrow('Maximum');
});
+ // @gate !disableLegacyMode
it('does not fall into an infinite update loop with useLayoutEffect', () => {
function NonTerminating() {
const [step, setStep] = React.useState(0);
@@ -1405,6 +1438,7 @@ describe('ReactLegacyUpdates', () => {
}).toThrow('Maximum');
});
+ // @gate !disableLegacyMode
it('can recover after falling into an infinite update loop', () => {
class NonTerminating extends React.Component {
state = {step: 0};
@@ -1445,6 +1479,7 @@ describe('ReactLegacyUpdates', () => {
expect(container.textContent).toBe('1');
});
+ // @gate !disableLegacyMode
it('does not fall into mutually recursive infinite update loop with same container', () => {
// Note: this test would fail if there were two or more different roots.
@@ -1472,6 +1507,7 @@ describe('ReactLegacyUpdates', () => {
}).toThrow('Maximum');
});
+ // @gate !disableLegacyMode
it('does not fall into an infinite error loop', () => {
function BadRender() {
throw new Error('error');
@@ -1505,6 +1541,7 @@ describe('ReactLegacyUpdates', () => {
}).toThrow('Maximum');
});
+ // @gate !disableLegacyMode
it('can schedule ridiculously many updates within the same batch without triggering a maximum update error', () => {
const subscribers = [];
@@ -1540,6 +1577,7 @@ describe('ReactLegacyUpdates', () => {
// TODO: Replace this branch with @gate pragmas
if (__DEV__) {
+ // @gate !disableLegacyMode
it('can have nested updates if they do not cross the limit', async () => {
let _setStep;
const LIMIT = 50;
@@ -1567,6 +1605,7 @@ describe('ReactLegacyUpdates', () => {
expect(container.textContent).toBe('50');
});
+ // @gate !disableLegacyMode
it('can have many updates inside useEffect without triggering a warning', async () => {
function Terminating() {
const [step, setStep] = React.useState(0);
diff --git a/packages/react-dom/src/__tests__/ReactMountDestruction-test.js b/packages/react-dom/src/__tests__/ReactMountDestruction-test.js
index cf5497a4220d6..6b9f97ce26bd7 100644
--- a/packages/react-dom/src/__tests__/ReactMountDestruction-test.js
+++ b/packages/react-dom/src/__tests__/ReactMountDestruction-test.js
@@ -49,6 +49,7 @@ describe('ReactMount', () => {
});
});
+ // @gate !disableLegacyMode
it('should warn when unmounting a non-container root node', () => {
const mainContainerDiv = document.createElement('div');
@@ -71,6 +72,7 @@ describe('ReactMount', () => {
);
});
+ // @gate !disableLegacyMode
it('should warn when unmounting a non-container, non-root node', () => {
const mainContainerDiv = document.createElement('div');
diff --git a/packages/react-dom/src/__tests__/ReactRenderDocument-test.js b/packages/react-dom/src/__tests__/ReactRenderDocument-test.js
index 3eab0e6c3661b..f901312677fad 100644
--- a/packages/react-dom/src/__tests__/ReactRenderDocument-test.js
+++ b/packages/react-dom/src/__tests__/ReactRenderDocument-test.js
@@ -373,6 +373,7 @@ describe('rendering React components at document', () => {
expect(testDocument.body.innerHTML).toBe('Hello world');
});
+ // @gate !disableLegacyMode
it('supports findDOMNode on full-page components in legacy mode', () => {
const tree = (
diff --git a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js
index 982166b40545e..872ef03f38ea6 100644
--- a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js
+++ b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js
@@ -505,6 +505,7 @@ describe('ReactDOMServerHydration', () => {
await act(() => root.render(
));
});
+ // @gate !disableLegacyMode
it('Suspense + hydration in legacy mode', () => {
const element = document.createElement('div');
element.innerHTML = '
';
@@ -530,6 +531,7 @@ describe('ReactDOMServerHydration', () => {
expect(element.innerHTML).toBe('
');
});
+ // @gate !disableLegacyMode
it('Suspense + hydration in legacy mode (at root)', () => {
const element = document.createElement('div');
element.innerHTML = '
Hello World
';
@@ -550,6 +552,7 @@ describe('ReactDOMServerHydration', () => {
);
});
+ // @gate !disableLegacyMode
it('Suspense + hydration in legacy mode with no fallback', () => {
const element = document.createElement('div');
element.innerHTML = '
Hello World
';
diff --git a/packages/react-dom/src/__tests__/findDOMNode-test.js b/packages/react-dom/src/__tests__/findDOMNode-test.js
index 59f819462421c..9713abe0150a7 100644
--- a/packages/react-dom/src/__tests__/findDOMNode-test.js
+++ b/packages/react-dom/src/__tests__/findDOMNode-test.js
@@ -19,6 +19,7 @@ describe('findDOMNode', () => {
expect(ReactDOM.findDOMNode(null)).toBe(null);
});
+ // @gate !disableLegacyMode
it('findDOMNode should find dom element', () => {
class MyNode extends React.Component {
render() {
@@ -37,6 +38,7 @@ describe('findDOMNode', () => {
expect(mySameDiv).toBe(myDiv);
});
+ // @gate !disableLegacyMode
it('findDOMNode should find dom element after an update from null', () => {
function Bar({flag}) {
if (flag) {
@@ -69,6 +71,7 @@ describe('findDOMNode', () => {
}).toThrowError('Argument appears to not be a ReactComponent. Keys: foo');
});
+ // @gate !disableLegacyMode
it('findDOMNode should reject unmounted objects with render func', () => {
class Foo extends React.Component {
render() {
@@ -85,6 +88,7 @@ describe('findDOMNode', () => {
);
});
+ // @gate !disableLegacyMode
it('findDOMNode should not throw an error when called within a component that is not mounted', () => {
class Bar extends React.Component {
UNSAFE_componentWillMount() {
@@ -98,6 +102,7 @@ describe('findDOMNode', () => {
expect(() => ReactTestUtils.renderIntoDocument(
)).not.toThrow();
});
+ // @gate !disableLegacyMode
it('findDOMNode should warn if used to find a host component inside StrictMode', () => {
let parent = undefined;
let child = undefined;
@@ -129,6 +134,7 @@ describe('findDOMNode', () => {
expect(match).toBe(child);
});
+ // @gate !disableLegacyMode
it('findDOMNode should warn if passed a component that is inside StrictMode', () => {
let parent = undefined;
let child = undefined;
diff --git a/packages/react-dom/src/__tests__/refsLegacy-test.js b/packages/react-dom/src/__tests__/refsLegacy-test.js
index c3af817892ffa..b207e01e76440 100644
--- a/packages/react-dom/src/__tests__/refsLegacy-test.js
+++ b/packages/react-dom/src/__tests__/refsLegacy-test.js
@@ -19,6 +19,7 @@ describe('root level refs with legacy APIs', () => {
ReactDOM = require('react-dom');
});
+ // @gate !disableLegacyMode
it('attaches and detaches root refs', () => {
let inst = null;
diff --git a/packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js b/packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js
index 4848fdebfd735..414dc85636cda 100644
--- a/packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js
+++ b/packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js
@@ -20,6 +20,7 @@ const renderSubtreeIntoContainer =
describe('renderSubtreeIntoContainer', () => {
// @gate !disableLegacyContext
+ // @gate !disableLegacyMode
it('should pass context when rendering subtree elsewhere', () => {
const portal = document.createElement('div');
@@ -63,46 +64,8 @@ describe('renderSubtreeIntoContainer', () => {
expect(portal.firstChild.innerHTML).toBe('bar');
});
- it('should throw if parentComponent is invalid', () => {
- const portal = document.createElement('div');
-
- class Component extends React.Component {
- static contextTypes = {
- foo: PropTypes.string.isRequired,
- };
-
- render() {
- return
{this.context.foo}
;
- }
- }
-
- // ESLint is confused here and thinks Parent is unused, presumably because
- // it is only used inside of the class body?
- // eslint-disable-next-line no-unused-vars
- class Parent extends React.Component {
- static childContextTypes = {
- foo: PropTypes.string.isRequired,
- };
-
- getChildContext() {
- return {
- foo: 'bar',
- };
- }
-
- render() {
- return null;
- }
-
- componentDidMount() {
- expect(function () {
- renderSubtreeIntoContainer(
,
, portal);
- }).toThrowError('parentComponentmust be a valid React Component');
- }
- }
- });
-
// @gate !disableLegacyContext
+ // @gate !disableLegacyMode
it('should update context if it changes due to setState', async () => {
const container = document.createElement('div');
document.body.appendChild(container);
@@ -171,6 +134,7 @@ describe('renderSubtreeIntoContainer', () => {
});
// @gate !disableLegacyContext
+ // @gate !disableLegacyMode
it('should update context if it changes due to re-render', async () => {
const container = document.createElement('div');
document.body.appendChild(container);
@@ -232,6 +196,7 @@ describe('renderSubtreeIntoContainer', () => {
expect(portal.firstChild.innerHTML).toBe('changed-changed');
});
+ // @gate !disableLegacyMode
it('should render portal with non-context-provider parent', async () => {
const container = document.createElement('div');
document.body.appendChild(container);
@@ -259,6 +224,7 @@ describe('renderSubtreeIntoContainer', () => {
});
// @gate !disableLegacyContext
+ // @gate !disableLegacyMode
it('should get context through non-context-provider parent', async () => {
const container = document.createElement('div');
document.body.appendChild(container);
@@ -306,6 +272,7 @@ describe('renderSubtreeIntoContainer', () => {
});
// @gate !disableLegacyContext
+ // @gate !disableLegacyMode
it('should get context through middle non-context-provider layer', async () => {
const container = document.createElement('div');
document.body.appendChild(container);
@@ -360,6 +327,7 @@ describe('renderSubtreeIntoContainer', () => {
expect(portal2.textContent).toBe('foo');
});
+ // @gate !disableLegacyMode
it('legacy test: fails gracefully when mixing React 15 and 16', () => {
class C extends React.Component {
render() {
diff --git a/packages/react-dom/src/client/ReactDOMLegacy.js b/packages/react-dom/src/client/ReactDOMLegacy.js
index 9a7934dc21576..c671e4d45ac6c 100644
--- a/packages/react-dom/src/client/ReactDOMLegacy.js
+++ b/packages/react-dom/src/client/ReactDOMLegacy.js
@@ -14,6 +14,7 @@ import type {
import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
import type {ReactNodeList} from 'shared/ReactTypes';
+import {disableLegacyMode} from 'shared/ReactFeatureFlags';
import {clearContainer} from 'react-dom-bindings/src/client/ReactFiberConfigDOM';
import {
getInstanceFromNode,
@@ -264,6 +265,14 @@ export function hydrate(
container: Container,
callback: ?Function,
): React$Component
| PublicInstance | null {
+ if (disableLegacyMode) {
+ if (__DEV__) {
+ console.error(
+ 'ReactDOM.hydrate is no longer supported in React 18. Use hydrateRoot instead',
+ );
+ }
+ throw new Error('ReactDOM: Unsupported Legacy Mode API.');
+ }
if (__DEV__) {
console.error(
'ReactDOM.hydrate is no longer supported in React 18. Use hydrateRoot ' +
@@ -304,6 +313,14 @@ export function render(
container: Container,
callback: ?Function,
): React$Component | PublicInstance | null {
+ if (disableLegacyMode) {
+ if (__DEV__) {
+ console.error(
+ 'ReactDOM.render is no longer supported in React 18. Use createRoot instead.',
+ );
+ }
+ throw new Error('ReactDOM: Unsupported Legacy Mode API.');
+ }
if (__DEV__) {
console.error(
'ReactDOM.render is no longer supported in React 18. Use createRoot ' +
@@ -344,6 +361,14 @@ export function unstable_renderSubtreeIntoContainer(
containerNode: Container,
callback: ?Function,
): React$Component | PublicInstance | null {
+ if (disableLegacyMode) {
+ if (__DEV__) {
+ console.error(
+ 'ReactDOM.unstable_renderSubtreeIntoContainer() is no longer supported in React 18. Consider using a portal instead.',
+ );
+ }
+ throw new Error('ReactDOM: Unsupported Legacy Mode API.');
+ }
if (__DEV__) {
console.error(
'ReactDOM.unstable_renderSubtreeIntoContainer() is no longer supported ' +
diff --git a/packages/react-dom/src/events/plugins/__tests__/ChangeEventPlugin-test.js b/packages/react-dom/src/events/plugins/__tests__/ChangeEventPlugin-test.js
index fab10a36e16d1..497f16a2abcec 100644
--- a/packages/react-dom/src/events/plugins/__tests__/ChangeEventPlugin-test.js
+++ b/packages/react-dom/src/events/plugins/__tests__/ChangeEventPlugin-test.js
@@ -433,7 +433,7 @@ describe('ChangeEventPlugin', () => {
expect(called2).toBe(1);
});
- it('should deduplicate input value change events', () => {
+ it('should deduplicate input value change events', async () => {
let called = 0;
function cb(e) {
@@ -441,59 +441,81 @@ describe('ChangeEventPlugin', () => {
expect(e.type).toBe('change');
}
- let input;
- ['text', 'number', 'range'].forEach(type => {
+ const inputTypes = ['text', 'number', 'range'];
+ while (inputTypes.length) {
+ const type = inputTypes.pop();
called = 0;
- input = ReactDOM.render(, container);
- // Should be ignored (no change):
- input.dispatchEvent(
- new Event('change', {bubbles: true, cancelable: true}),
- );
- setUntrackedValue.call(input, '42');
- input.dispatchEvent(
- new Event('change', {bubbles: true, cancelable: true}),
- );
- // Should be ignored (no change):
- input.dispatchEvent(
- new Event('change', {bubbles: true, cancelable: true}),
- );
+ let root = ReactDOMClient.createRoot(container);
+ let ref = {current: null};
+ await act(() => {
+ root.render();
+ });
+ let input = ref.current;
+ await act(() => {
+ // Should be ignored (no change):
+ input.dispatchEvent(
+ new Event('change', {bubbles: true, cancelable: true}),
+ );
+ setUntrackedValue.call(input, '42');
+ input.dispatchEvent(
+ new Event('change', {bubbles: true, cancelable: true}),
+ );
+ // Should be ignored (no change):
+ input.dispatchEvent(
+ new Event('change', {bubbles: true, cancelable: true}),
+ );
+ });
expect(called).toBe(1);
- ReactDOM.unmountComponentAtNode(container);
+ root.unmount();
called = 0;
- input = ReactDOM.render(, container);
- // Should be ignored (no change):
- input.dispatchEvent(
- new Event('input', {bubbles: true, cancelable: true}),
- );
- setUntrackedValue.call(input, '42');
- input.dispatchEvent(
- new Event('input', {bubbles: true, cancelable: true}),
- );
- // Should be ignored (no change):
- input.dispatchEvent(
- new Event('input', {bubbles: true, cancelable: true}),
- );
+ root = ReactDOMClient.createRoot(container);
+ ref = {current: null};
+ await act(() => {
+ root.render();
+ });
+ input = ref.current;
+ await act(() => {
+ // Should be ignored (no change):
+ input.dispatchEvent(
+ new Event('input', {bubbles: true, cancelable: true}),
+ );
+ setUntrackedValue.call(input, '42');
+ input.dispatchEvent(
+ new Event('input', {bubbles: true, cancelable: true}),
+ );
+ // Should be ignored (no change):
+ input.dispatchEvent(
+ new Event('input', {bubbles: true, cancelable: true}),
+ );
+ });
expect(called).toBe(1);
- ReactDOM.unmountComponentAtNode(container);
+ root.unmount();
called = 0;
- input = ReactDOM.render(, container);
- // Should be ignored (no change):
- input.dispatchEvent(
- new Event('change', {bubbles: true, cancelable: true}),
- );
- setUntrackedValue.call(input, '42');
- input.dispatchEvent(
- new Event('input', {bubbles: true, cancelable: true}),
- );
- // Should be ignored (no change):
- input.dispatchEvent(
- new Event('change', {bubbles: true, cancelable: true}),
- );
+ root = ReactDOMClient.createRoot(container);
+ ref = {current: null};
+ await act(() => {
+ root.render();
+ });
+ input = ref.current;
+ await act(() => {
+ // Should be ignored (no change):
+ input.dispatchEvent(
+ new Event('change', {bubbles: true, cancelable: true}),
+ );
+ setUntrackedValue.call(input, '42');
+ input.dispatchEvent(
+ new Event('input', {bubbles: true, cancelable: true}),
+ );
+ // Should be ignored (no change):
+ input.dispatchEvent(
+ new Event('change', {bubbles: true, cancelable: true}),
+ );
+ });
expect(called).toBe(1);
- ReactDOM.unmountComponentAtNode(container);
- });
+ root.unmount();
+ }
});
it('should listen for both change and input events when supported', async () => {
diff --git a/packages/react-dom/src/events/plugins/__tests__/EnterLeaveEventPlugin-test.js b/packages/react-dom/src/events/plugins/__tests__/EnterLeaveEventPlugin-test.js
index 569a29a22a5b8..b25d6bd3ed66a 100644
--- a/packages/react-dom/src/events/plugins/__tests__/EnterLeaveEventPlugin-test.js
+++ b/packages/react-dom/src/events/plugins/__tests__/EnterLeaveEventPlugin-test.js
@@ -158,7 +158,8 @@ describe('EnterLeaveEventPlugin', () => {
});
// Test for https://github.com/facebook/react/issues/16763.
- it('should call mouseEnter once from sibling rendered inside a rendered component in legacy roots', done => {
+ // @gate !disableLegacyMode
+ it('should call mouseEnter once from sibling rendered inside a rendered component in legacy roots', async () => {
const mockFn = jest.fn();
class Parent extends React.Component {
@@ -191,8 +192,6 @@ describe('EnterLeaveEventPlugin', () => {
relatedTarget: this.firstEl.current,
}),
);
- expect(mockFn.mock.calls.length).toBe(1);
- done();
}
render() {
@@ -205,10 +204,14 @@ describe('EnterLeaveEventPlugin', () => {
}
}
- ReactDOM.render(, container);
+ await act(() => {
+ ReactDOM.render(, container);
+ });
+ expect(mockFn.mock.calls.length).toBe(1);
});
- it('should call mouseEnter when pressing a non tracked React node in legacy root', done => {
+ // @gate !disableLegacyMode
+ it('should call mouseEnter when pressing a non tracked React node in legacy root', async () => {
const mockFn = jest.fn();
class Parent extends React.Component {
@@ -243,8 +246,6 @@ describe('EnterLeaveEventPlugin', () => {
relatedTarget: this.siblingEl.current,
}),
);
- expect(mockFn.mock.calls.length).toBe(1);
- done();
}
render() {
@@ -256,7 +257,10 @@ describe('EnterLeaveEventPlugin', () => {
}
}
- ReactDOM.render(, container);
+ await act(() => {
+ ReactDOM.render(, container);
+ });
+ expect(mockFn.mock.calls.length).toBe(1);
});
it('should work with portals outside of the root that has onMouseLeave', async () => {
diff --git a/packages/react-dom/unstable_testing.experimental.js b/packages/react-dom/unstable_testing.experimental.js
index b84561272bef3..a7b01b449639c 100644
--- a/packages/react-dom/unstable_testing.experimental.js
+++ b/packages/react-dom/unstable_testing.experimental.js
@@ -11,11 +11,7 @@ export {
createPortal,
findDOMNode,
flushSync,
- hydrate,
- render,
- unmountComponentAtNode,
unstable_batchedUpdates,
- unstable_renderSubtreeIntoContainer,
useFormStatus,
useFormState,
prefetchDNS,
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
index ab68bdc7817cb..32ed2c1b3e34e 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
@@ -399,6 +399,7 @@ describe('ReactSuspense', () => {
},
);
+ // @gate !disableLegacyMode
it('mounts a lazy class component in non-concurrent mode (legacy)', async () => {
class Class extends React.Component {
componentDidMount() {
@@ -685,6 +686,7 @@ describe('ReactSuspense', () => {
});
describe('outside concurrent mode (legacy)', () => {
+ // @gate !disableLegacyMode
it('a mounted class component can suspend without losing state', async () => {
class TextWithLifecycle extends React.Component {
componentDidMount() {
@@ -763,6 +765,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('AB:2C');
});
+ // @gate !disableLegacyMode
it('bails out on timed-out primary children even if they receive an update', async () => {
let instance;
class Stateful extends React.Component {
@@ -803,6 +806,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('Stateful: 2B');
});
+ // @gate !disableLegacyMode
it('when updating a timed-out tree, always retries the suspended component', async () => {
let instance;
class Stateful extends React.Component {
@@ -858,6 +862,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('Stateful: 2B');
});
+ // @gate !disableLegacyMode
it('suspends in a class that has componentWillUnmount and is then deleted', async () => {
class AsyncTextWithUnmount extends React.Component {
componentWillUnmount() {
@@ -884,6 +889,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('B');
});
+ // @gate !disableLegacyMode
it('suspends in a component that also contains useEffect', async () => {
const {useLayoutEffect} = React;
@@ -911,6 +917,7 @@ describe('ReactSuspense', () => {
assertLog(['A', 'Did commit: A']);
});
+ // @gate !disableLegacyMode
it('retries when an update is scheduled on a timed out tree', async () => {
let instance;
class Stateful extends React.Component {
@@ -958,6 +965,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('Step: 3');
});
+ // @gate !disableLegacyMode
it('does not remount the fallback while suspended children resolve in legacy mode', async () => {
let mounts = 0;
class ShouldMountOnce extends React.Component {
@@ -1004,6 +1012,7 @@ describe('ReactSuspense', () => {
expect(mounts).toBe(1);
});
+ // @gate !disableLegacyMode
it('reuses effects, including deletions, from the suspended tree', async () => {
const {useState} = React;
@@ -1045,6 +1054,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('Tab: 2 + sibling');
});
+ // @gate !disableLegacyMode
it('does not warn if a mounted component is pinged', async () => {
const {useState} = React;
@@ -1076,6 +1086,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('Loading...');
});
+ // @gate !disableLegacyMode
it('memoizes promise listeners per thread ID to prevent redundant renders', async () => {
function App() {
return (
@@ -1118,6 +1129,7 @@ describe('ReactSuspense', () => {
]);
});
+ // @gate !disableLegacyMode
it('#14162', async () => {
const {lazy} = React;
@@ -1155,6 +1167,7 @@ describe('ReactSuspense', () => {
ReactDOM.render(, container);
});
+ // @gate !disableLegacyMode
it('updates memoized child of suspense component when context updates (simple memo)', async () => {
const {useContext, createContext, useState, memo} = React;
@@ -1194,6 +1207,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('new value');
});
+ // @gate !disableLegacyMode
it('updates memoized child of suspense component when context updates (manual memo)', async () => {
const {useContext, createContext, useState, memo} = React;
@@ -1238,6 +1252,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('new value');
});
+ // @gate !disableLegacyMode
it('updates memoized child of suspense component when context updates (function)', async () => {
const {useContext, createContext, useState} = React;
@@ -1282,6 +1297,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('new value');
});
+ // @gate !disableLegacyMode
it('updates memoized child of suspense component when context updates (forwardRef)', async () => {
const {forwardRef, useContext, createContext, useState} = React;
@@ -1321,6 +1337,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('new value');
});
+ // @gate !disableLegacyMode
it('updates context consumer within child of suspended suspense component when context updates', async () => {
const {createContext, useState} = React;
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemanticsDOM-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemanticsDOM-test.js
index 2a6b02acfe4f3..e351052a4a22d 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemanticsDOM-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemanticsDOM-test.js
@@ -448,6 +448,7 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => {
expect(container.innerHTML).toBe('Hello
');
});
+ // @gate !disableLegacyMode
it('regression: unmount hidden tree, in legacy mode', async () => {
// In legacy mode, when a tree suspends and switches to a fallback, the
// effects are not unmounted. So we have to unmount them during a deletion.
diff --git a/packages/react-reconciler/src/__tests__/ReactUpdaters-test.internal.js b/packages/react-reconciler/src/__tests__/ReactUpdaters-test.internal.js
index 1727a595365ef..a14e29daaf332 100644
--- a/packages/react-reconciler/src/__tests__/ReactUpdaters-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactUpdaters-test.internal.js
@@ -262,6 +262,9 @@ describe('updaters', () => {
await waitForAll([]);
});
+ // This test should be convertable to createRoot but the allScheduledTypes assertions are no longer the same
+ // So I'm leaving it in legacy mode for now and just disabling if legacy mode is turned off
+ // @gate !disableLegacyMode
it('should cover suspense pings', async () => {
let data = null;
let resolver = null;
diff --git a/packages/react/src/__tests__/ReactStrictMode-test.js b/packages/react/src/__tests__/ReactStrictMode-test.js
index 32f4682385c88..bd4b6cf90cb3d 100644
--- a/packages/react/src/__tests__/ReactStrictMode-test.js
+++ b/packages/react/src/__tests__/ReactStrictMode-test.js
@@ -77,6 +77,7 @@ describe('ReactStrictMode', () => {
});
// @gate __DEV__
+ // @gate !disableLegacyMode
it('should invoke only precommit lifecycle methods twice in legacy roots', async () => {
let log = [];
let shouldComponentUpdate = false;
@@ -242,6 +243,7 @@ describe('ReactStrictMode', () => {
]);
});
+ // @gate !disableLegacyMode
it('should invoke only precommit lifecycle methods twice in DEV legacy roots', async () => {
const {StrictMode} = React;
diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js
index 2d48410460b87..506f6746b98de 100644
--- a/packages/shared/ReactFeatureFlags.js
+++ b/packages/shared/ReactFeatureFlags.js
@@ -195,6 +195,11 @@ export const disableStringRefs = __NEXT_MAJOR__;
// Warn on any usage of ReactTestRenderer
export const enableReactTestRendererWarning = false;
+// Disables legacy mode
+// This allows us to land breaking changes to remove legacy mode APIs in experimental builds
+// before removing them in stable in the next Major
+export const disableLegacyMode = __NEXT_MAJOR__;
+
// -----------------------------------------------------------------------------
// Chopping Block
//
diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js
index afc64995ee2c0..5057b9d3cdf63 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-fb.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js
@@ -104,6 +104,7 @@ export const enableRefAsProp = false;
export const disableStringRefs = false;
export const enableReactTestRendererWarning = false;
+export const disableLegacyMode = false;
export const enableBigIntSupport = false;
diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js
index dcc8fd7b95f24..1db47db31b857 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-oss.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js
@@ -97,6 +97,7 @@ export const disableStringRefs = false;
export const enableReactTestRendererWarning = false;
export const enableBigIntSupport = false;
+export const disableLegacyMode = false;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
index e82f84389e544..60e53b3fb3315 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
@@ -101,6 +101,7 @@ export const enableRefAsProp = __NEXT_MAJOR__;
export const disableStringRefs = __NEXT_MAJOR__;
export const enableReactTestRendererWarning = false;
export const enableBigIntSupport = __NEXT_MAJOR__;
+export const disableLegacyMode = __NEXT_MAJOR__;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
index 642f2c3f08362..5de35a8b21ee2 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
@@ -91,6 +91,7 @@ export const enableRefAsProp = false;
export const disableStringRefs = false;
export const enableReactTestRendererWarning = false;
+export const disableLegacyMode = false;
export const enableBigIntSupport = false;
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
index b276f804cc6bf..fbf88c8dbabac 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
@@ -94,6 +94,7 @@ export const enableRefAsProp = false;
export const disableStringRefs = false;
export const enableReactTestRendererWarning = false;
+export const disableLegacyMode = false;
export const enableBigIntSupport = false;
diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js
index d8df7644fca52..4d77c65f4017e 100644
--- a/packages/shared/forks/ReactFeatureFlags.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.www.js
@@ -126,5 +126,7 @@ export const enableBigIntSupport = false;
// because JSX is an extremely hot path.
export const disableStringRefs = false;
+export const disableLegacyMode = false;
+
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json
index 1471b340098a0..ab76c89d191c5 100644
--- a/scripts/error-codes/codes.json
+++ b/scripts/error-codes/codes.json
@@ -493,5 +493,6 @@
"505": "Cannot render an Async Component, Promise or React.Lazy inside React.Children. We recommend not iterating over children and just rendering them plain.",
"506": "Functions are not valid as a child of Client Components. This may happen if you return %s instead of <%s /> from render. Or maybe you meant to call this function rather than return it.%s",
"507": "Expected the last optional `callback` argument to be a function. Instead received: %s.",
- "508": "The first argument must be a React class instance. Instead received: %s."
+ "508": "The first argument must be a React class instance. Instead received: %s.",
+ "509": "ReactDOM: Unsupported Legacy Mode API."
}