Convert ReactStrictMode to createRoot (#28162)

This commit is contained in:
Sebastian Silbermann
2024-02-02 08:57:56 +01:00
committed by GitHub
parent b25dcd3958
commit fa8a34bfac
@@ -34,19 +34,21 @@ describe('ReactStrictMode', () => {
useReducer = React.useReducer;
});
it('should appear in the client component stack', () => {
it('should appear in the client component stack', async () => {
function Foo() {
return <div ariaTypo="" />;
}
const container = document.createElement('div');
expect(() => {
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
const root = ReactDOMClient.createRoot(container);
await expect(async () => {
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
}).toErrorDev(
'Invalid ARIA attribute `ariaTypo`. ' +
'ARIA attributes follow the pattern aria-* and must be lowercase.\n' +
@@ -75,7 +77,7 @@ describe('ReactStrictMode', () => {
});
// @gate __DEV__
it('should invoke precommit lifecycle methods twice', () => {
it('should invoke only precommit lifecycle methods twice in legacy roots', async () => {
let log = [];
let shouldComponentUpdate = false;
class ClassComponent extends React.Component {
@@ -162,7 +164,7 @@ describe('ReactStrictMode', () => {
]);
});
it('should invoke setState callbacks twice', () => {
it('should invoke setState callbacks twice', async () => {
let instance;
class ClassComponent extends React.Component {
state = {
@@ -177,17 +179,21 @@ describe('ReactStrictMode', () => {
let setStateCount = 0;
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<ClassComponent />
</React.StrictMode>,
container,
);
instance.setState(state => {
setStateCount++;
return {
count: state.count + 1,
};
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<ClassComponent />
</React.StrictMode>,
);
});
await act(() => {
instance.setState(state => {
setStateCount++;
return {
count: state.count + 1,
};
});
});
// Callback should be invoked twice in DEV
@@ -196,7 +202,7 @@ describe('ReactStrictMode', () => {
expect(instance.state.count).toBe(2);
});
it('should invoke precommit lifecycle methods twice in DEV', () => {
it('should invoke only precommit lifecycle methods twice in DEV legacy roots', async () => {
const {StrictMode} = React;
let log = [];
@@ -303,7 +309,7 @@ describe('ReactStrictMode', () => {
}
});
it('should invoke setState callbacks twice in DEV', () => {
it('should invoke setState callbacks twice in DEV', async () => {
const {StrictMode} = React;
let instance;
@@ -320,17 +326,21 @@ describe('ReactStrictMode', () => {
let setStateCount = 0;
const container = document.createElement('div');
ReactDOM.render(
<StrictMode>
<ClassComponent />
</StrictMode>,
container,
);
instance.setState(state => {
setStateCount++;
return {
count: state.count + 1,
};
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<StrictMode>
<ClassComponent />
</StrictMode>,
);
});
await act(() => {
instance.setState(state => {
setStateCount++;
return {
count: state.count + 1,
};
});
});
// Callback should be invoked twice (in DEV)
@@ -522,7 +532,6 @@ describe('Concurrent Mode', () => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
act = require('internal-test-utils').act;
});
@@ -727,7 +736,7 @@ Please update the following components: Parent`,
await act(() => root.render(<StrictRoot foo={false} />));
});
it('should also warn inside of "strict" mode trees', () => {
it('should also warn inside of "strict" mode trees', async () => {
const {StrictMode} = React;
class SyncRoot extends React.Component {
@@ -765,13 +774,20 @@ Please update the following components: Parent`,
const container = document.createElement('div');
expect(() => ReactDOM.render(<SyncRoot />, container)).toErrorDev(
const root = ReactDOMClient.createRoot(container);
await expect(async () => {
await act(() => {
root.render(<SyncRoot />);
});
}).toErrorDev(
'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
{withoutStack: true},
);
// Dedupe
ReactDOM.render(<SyncRoot />, container);
await act(() => {
root.render(<SyncRoot />);
});
});
});
@@ -779,11 +795,11 @@ describe('symbol checks', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
act = require('internal-test-utils').act;
});
it('should switch from StrictMode to a Fragment and reset state', () => {
it('should switch from StrictMode to a Fragment and reset state', async () => {
const {Fragment, StrictMode} = React;
function ParentComponent({useFragment}) {
@@ -813,13 +829,18 @@ describe('symbol checks', () => {
}
const container = document.createElement('div');
ReactDOM.render(<ParentComponent useFragment={false} />, container);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(<ParentComponent useFragment={false} />);
});
expect(container.textContent).toBe('count:1');
ReactDOM.render(<ParentComponent useFragment={true} />, container);
await act(() => {
root.render(<ParentComponent useFragment={true} />);
});
expect(container.textContent).toBe('count:1');
});
it('should switch from a Fragment to StrictMode and reset state', () => {
it('should switch from a Fragment to StrictMode and reset state', async () => {
const {Fragment, StrictMode} = React;
function ParentComponent({useFragment}) {
@@ -849,13 +870,18 @@ describe('symbol checks', () => {
}
const container = document.createElement('div');
ReactDOM.render(<ParentComponent useFragment={true} />, container);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(<ParentComponent useFragment={true} />);
});
expect(container.textContent).toBe('count:1');
ReactDOM.render(<ParentComponent useFragment={false} />, container);
await act(() => {
root.render(<ParentComponent useFragment={false} />);
});
expect(container.textContent).toBe('count:1');
});
it('should update with StrictMode without losing state', () => {
it('should update with StrictMode without losing state', async () => {
const {StrictMode} = React;
function ParentComponent() {
@@ -881,9 +907,14 @@ describe('symbol checks', () => {
}
const container = document.createElement('div');
ReactDOM.render(<ParentComponent />, container);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(<ParentComponent />);
});
expect(container.textContent).toBe('count:1');
ReactDOM.render(<ParentComponent />, container);
await act(() => {
root.render(<ParentComponent />);
});
expect(container.textContent).toBe('count:2');
});
});
@@ -894,9 +925,10 @@ describe('string refs', () => {
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
act = require('internal-test-utils').act;
});
it('should warn within a strict tree', () => {
it('should warn within a strict tree', async () => {
const {StrictMode} = React;
class OuterComponent extends React.Component {
@@ -916,8 +948,11 @@ describe('string refs', () => {
}
const container = document.createElement('div');
expect(() => {
ReactDOM.render(<OuterComponent />, container);
const root = ReactDOMClient.createRoot(container);
await expect(async () => {
await act(() => {
root.render(<OuterComponent />);
});
}).toErrorDev(
'Warning: Component "StrictMode" contains the string ref "somestring". ' +
'Support for string refs will be removed in a future major release. ' +
@@ -926,49 +961,47 @@ describe('string refs', () => {
' in OuterComponent (at **)',
);
// Dedup
ReactDOM.render(<OuterComponent />, container);
await act(() => {
root.render(<OuterComponent />);
});
});
it('should warn within a strict tree', () => {
it('should warn within a strict tree', async () => {
const {StrictMode} = React;
class OuterComponent extends React.Component {
render() {
return (
<StrictMode>
<InnerComponent />
<InnerComponent ref="somestring" />
</StrictMode>
);
}
}
class InnerComponent extends React.Component {
render() {
return <MiddleComponent ref="somestring" />;
}
}
class MiddleComponent extends React.Component {
render() {
return null;
}
}
const container = document.createElement('div');
expect(() => {
ReactDOM.render(<OuterComponent />, container);
const root = ReactDOMClient.createRoot(container);
await expect(async () => {
await act(() => {
root.render(<OuterComponent />);
});
}).toErrorDev(
'Warning: Component "InnerComponent" contains the string ref "somestring". ' +
'Warning: Component "StrictMode" 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://reactjs.org/link/strict-mode-string-ref\n' +
' in InnerComponent (at **)\n' +
' in OuterComponent (at **)',
);
// Dedup
ReactDOM.render(<OuterComponent />, container);
await act(() => {
root.render(<OuterComponent />);
});
});
});
@@ -976,8 +1009,8 @@ describe('context legacy', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
act = require('internal-test-utils').act;
PropTypes = require('prop-types');
});
@@ -986,7 +1019,7 @@ describe('context legacy', () => {
});
// @gate !disableLegacyContext || !__DEV__
it('should warn if the legacy context API have been used in strict mode', () => {
it('should warn if the legacy context API have been used in strict mode', async () => {
class LegacyContextProvider extends React.Component {
getChildContext() {
return {color: 'purple'};
@@ -1039,8 +1072,11 @@ describe('context legacy', () => {
};
const container = document.createElement('div');
expect(() => {
ReactDOM.render(<Root />, container);
const root = ReactDOMClient.createRoot(container);
await expect(async () => {
await act(() => {
root.render(<Root />);
});
}).toErrorDev(
'Warning: Legacy context API has been detected within a strict-mode tree.' +
'\n\nThe old API will be supported in all 16.x releases, but applications ' +
@@ -1055,19 +1091,21 @@ describe('context legacy', () => {
);
// Dedupe
ReactDOM.render(<Root />, container);
await act(() => {
root.render(<Root />);
});
});
describe('console logs logging', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
act = require('internal-test-utils').act;
});
if (ReactFeatureFlags.consoleManagedByDevToolsDuringStrictMode) {
it('does not disable logs for class double render', () => {
it('does not disable logs for class double render', async () => {
spyOnDevAndProd(console, 'log');
let count = 0;
@@ -1080,13 +1118,14 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(__DEV__ ? 2 : 1);
// Note: we should display the first log because otherwise
@@ -1095,7 +1134,7 @@ describe('context legacy', () => {
expect(console.log).toBeCalledWith('foo 1');
});
it('does not disable logs for class double ctor', () => {
it('does not disable logs for class double ctor', async () => {
spyOnDevAndProd(console, 'log');
let count = 0;
@@ -1111,13 +1150,14 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(__DEV__ ? 2 : 1);
// Note: we should display the first log because otherwise
@@ -1126,7 +1166,7 @@ describe('context legacy', () => {
expect(console.log).toBeCalledWith('foo 1');
});
it('does not disable logs for class double getDerivedStateFromProps', () => {
it('does not disable logs for class double getDerivedStateFromProps', async () => {
spyOnDevAndProd(console, 'log');
let count = 0;
@@ -1143,13 +1183,14 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(__DEV__ ? 2 : 1);
// Note: we should display the first log because otherwise
@@ -1158,7 +1199,7 @@ describe('context legacy', () => {
expect(console.log).toBeCalledWith('foo 1');
});
it('does not disable logs for class double shouldComponentUpdate', () => {
it('does not disable logs for class double shouldComponentUpdate', async () => {
spyOnDevAndProd(console, 'log');
let count = 0;
@@ -1175,19 +1216,21 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
// Trigger sCU:
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(__DEV__ ? 2 : 1);
@@ -1197,7 +1240,7 @@ describe('context legacy', () => {
expect(console.log).toBeCalledWith('foo 1');
});
it('does not disable logs for class state updaters', () => {
it('does not disable logs for class state updaters', async () => {
spyOnDevAndProd(console, 'log');
let inst;
@@ -1211,16 +1254,20 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
inst.setState(() => {
count++;
console.log('foo ' + count);
return {};
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
await act(() => {
inst.setState(() => {
count++;
console.log('foo ' + count);
return {};
});
});
expect(count).toBe(__DEV__ ? 2 : 1);
@@ -1231,7 +1278,7 @@ describe('context legacy', () => {
expect(console.log).toBeCalledWith('foo 1');
});
it('does not disable logs for function double render', () => {
it('does not disable logs for function double render', async () => {
spyOnDevAndProd(console, 'log');
let count = 0;
@@ -1242,13 +1289,14 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(__DEV__ ? 2 : 1);
// Note: we should display the first log because otherwise
@@ -1257,7 +1305,7 @@ describe('context legacy', () => {
expect(console.log).toBeCalledWith('foo 1');
});
} else {
it('disable logs for class double render', () => {
it('disable logs for class double render', async () => {
spyOnDevAndProd(console, 'log');
let count = 0;
@@ -1270,13 +1318,14 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(1);
// Note: we should display the first log because otherwise
@@ -1285,7 +1334,7 @@ describe('context legacy', () => {
expect(console.log).toBeCalledWith('foo 1');
});
it('disables logs for class double ctor', () => {
it('disables logs for class double ctor', async () => {
spyOnDevAndProd(console, 'log');
let count = 0;
@@ -1301,13 +1350,14 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(1);
// Note: we should display the first log because otherwise
@@ -1316,7 +1366,7 @@ describe('context legacy', () => {
expect(console.log).toBeCalledWith('foo 1');
});
it('disable logs for class double getDerivedStateFromProps', () => {
it('disable logs for class double getDerivedStateFromProps', async () => {
spyOnDevAndProd(console, 'log');
let count = 0;
@@ -1333,13 +1383,14 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(1);
// Note: we should display the first log because otherwise
@@ -1348,7 +1399,7 @@ describe('context legacy', () => {
expect(console.log).toBeCalledWith('foo 1');
});
it('disable logs for class double shouldComponentUpdate', () => {
it('disable logs for class double shouldComponentUpdate', async () => {
spyOnDevAndProd(console, 'log');
let count = 0;
@@ -1365,20 +1416,21 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
// Trigger sCU:
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(1);
// Note: we should display the first log because otherwise
@@ -1387,7 +1439,7 @@ describe('context legacy', () => {
expect(console.log).toBeCalledWith('foo 1');
});
it('disable logs for class state updaters', () => {
it('disable logs for class state updaters', async () => {
spyOnDevAndProd(console, 'log');
let inst;
@@ -1401,16 +1453,20 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
inst.setState(() => {
count++;
console.log('foo ' + count);
return {};
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
await act(() => {
inst.setState(() => {
count++;
console.log('foo ' + count);
return {};
});
});
expect(count).toBe(__DEV__ ? 2 : 1);
@@ -1421,7 +1477,7 @@ describe('context legacy', () => {
expect(console.log).toBeCalledWith('foo 1');
});
it('disable logs for function double render', () => {
it('disable logs for function double render', async () => {
spyOnDevAndProd(console, 'log');
let count = 0;
@@ -1432,13 +1488,14 @@ describe('context legacy', () => {
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(1);
// Note: we should display the first log because otherwise