'use strict';
let React;
let ReactDOM;
let ReactDOMClient;
let ReactDOMServer;
let PropTypes;
let act;
let useMemo;
let useState;
let useReducer;
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
describe('ReactStrictMode', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
act = require('internal-test-utils').act;
useMemo = React.useMemo;
useState = React.useState;
useReducer = React.useReducer;
});
it('should appear in the client component stack', () => {
function Foo() {
return <div ariaTypo="" />;
}
const container = document.createElement('div');
expect(() => {
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
}).toErrorDev(
'Invalid ARIA attribute `ariaTypo`. ' +
'ARIA attributes follow the pattern aria-* and must be lowercase.\n' +
' in div (at **)\n' +
' in Foo (at **)',
);
});
it('should appear in the SSR component stack', () => {
function Foo() {
return <div ariaTypo="" />;
}
expect(() => {
ReactDOMServer.renderToString(
<React.StrictMode>
<Foo />
</React.StrictMode>,
);
}).toErrorDev(
'Invalid ARIA attribute `ariaTypo`. ' +
'ARIA attributes follow the pattern aria-* and must be lowercase.\n' +
' in div (at **)\n' +
' in Foo (at **)',
);
});
it('should invoke precommit lifecycle methods twice', () => {
let log = [];
let shouldComponentUpdate = false;
class ClassComponent extends React.Component {
state = {};
static getDerivedStateFromProps() {
log.push('getDerivedStateFromProps');
return null;
}
constructor(props) {
super(props);
log.push('constructor');
}
componentDidMount() {
log.push('componentDidMount');
}
componentDidUpdate() {
log.push('componentDidUpdate');
}
componentWillUnmount() {
log.push('componentWillUnmount');
}
shouldComponentUpdate() {
log.push('shouldComponentUpdate');
return shouldComponentUpdate;
}
render() {
log.push('render');
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<ClassComponent />
</React.StrictMode>,
container,
);
expect(log).toEqual([
'constructor',
'constructor',
'getDerivedStateFromProps',
'getDerivedStateFromProps',
'render',
'render',
'componentDidMount',
]);
log = [];
shouldComponentUpdate = true;
ReactDOM.render(
<React.StrictMode>
<ClassComponent />
</React.StrictMode>,
container,
);
expect(log).toEqual([
'getDerivedStateFromProps',
'getDerivedStateFromProps',
'shouldComponentUpdate',
'shouldComponentUpdate',
'render',
'render',
'componentDidUpdate',
]);
log = [];
shouldComponentUpdate = false;
ReactDOM.render(
<React.StrictMode>
<ClassComponent />
</React.StrictMode>,
container,
);
expect(log).toEqual([
'getDerivedStateFromProps',
'getDerivedStateFromProps',
'shouldComponentUpdate',
'shouldComponentUpdate',
]);
});
it('should invoke setState callbacks twice', () => {
let instance;
class ClassComponent extends React.Component {
state = {
count: 1,
};
render() {
instance = this;
return null;
}
}
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,
};
});
expect(setStateCount).toBe(__DEV__ ? 2 : 1);
expect(instance.state.count).toBe(2);
});
it('should invoke precommit lifecycle methods twice in DEV', () => {
const {StrictMode} = React;
let log = [];
let shouldComponentUpdate = false;
function Root() {
return (
<StrictMode>
<ClassComponent />
</StrictMode>
);
}
class ClassComponent extends React.Component {
state = {};
static getDerivedStateFromProps() {
log.push('getDerivedStateFromProps');
return null;
}
constructor(props) {
super(props);
log.push('constructor');
}
componentDidMount() {
log.push('componentDidMount');
}
componentDidUpdate() {
log.push('componentDidUpdate');
}
componentWillUnmount() {
log.push('componentWillUnmount');
}
shouldComponentUpdate() {
log.push('shouldComponentUpdate');
return shouldComponentUpdate;
}
render() {
log.push('render');
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(<Root />, container);
if (__DEV__) {
expect(log).toEqual([
'constructor',
'constructor',
'getDerivedStateFromProps',
'getDerivedStateFromProps',
'render',
'render',
'componentDidMount',
]);
} else {
expect(log).toEqual([
'constructor',
'getDerivedStateFromProps',
'render',
'componentDidMount',
]);
}
log = [];
shouldComponentUpdate = true;
ReactDOM.render(<Root />, container);
if (__DEV__) {
expect(log).toEqual([
'getDerivedStateFromProps',
'getDerivedStateFromProps',
'shouldComponentUpdate',
'shouldComponentUpdate',
'render',
'render',
'componentDidUpdate',
]);
} else {
expect(log).toEqual([
'getDerivedStateFromProps',
'shouldComponentUpdate',
'render',
'componentDidUpdate',
]);
}
log = [];
shouldComponentUpdate = false;
ReactDOM.render(<Root />, container);
if (__DEV__) {
expect(log).toEqual([
'getDerivedStateFromProps',
'getDerivedStateFromProps',
'shouldComponentUpdate',
'shouldComponentUpdate',
]);
} else {
expect(log).toEqual([
'getDerivedStateFromProps',
'shouldComponentUpdate',
]);
}
});
it('should invoke setState callbacks twice in DEV', () => {
const {StrictMode} = React;
let instance;
class ClassComponent extends React.Component {
state = {
count: 1,
};
render() {
instance = this;
return null;
}
}
let setStateCount = 0;
const container = document.createElement('div');
ReactDOM.render(
<StrictMode>
<ClassComponent />
</StrictMode>,
container,
);
instance.setState(state => {
setStateCount++;
return {
count: state.count + 1,
};
});
expect(setStateCount).toBe(__DEV__ ? 2 : 1);
expect(instance.state.count).toBe(2);
});
it('double invokes useMemo functions', async () => {
let log = [];
function Uppercased({text}) {
return useMemo(() => {
const uppercased = text.toUpperCase();
log.push('Compute toUpperCase: ' + uppercased);
return uppercased;
}, [text]);
}
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Uppercased text="hello" />
</React.StrictMode>,
);
});
expect(container.textContent).toBe('HELLO');
expect(log).toEqual([
'Compute toUpperCase: HELLO',
'Compute toUpperCase: HELLO',
]);
log = [];
await act(() => {
root.render(
<React.StrictMode>
<Uppercased text="goodbye" />
</React.StrictMode>,
);
});
expect(container.textContent).toBe('GOODBYE');
expect(log).toEqual([
'Compute toUpperCase: GOODBYE',
'Compute toUpperCase: GOODBYE',
]);
});
it('double invokes useMemo functions', async () => {
let log = [];
function Uppercased({text}) {
const memoizedResult = useMemo(() => {
const uppercased = text.toUpperCase();
log.push('Compute toUpperCase: ' + uppercased);
return {uppercased};
}, [text]);
log.push(memoizedResult);
return memoizedResult.uppercased;
}
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<Uppercased text="hello" />
</React.StrictMode>,
);
});
expect(container.textContent).toBe('HELLO');
expect(log).toEqual([
'Compute toUpperCase: HELLO',
'Compute toUpperCase: HELLO',
{uppercased: 'HELLO'},
{uppercased: 'HELLO'},
]);
expect(log[2]).toBe(log[3]);
log = [];
await act(() => {
root.render(
<React.StrictMode>
<Uppercased text="goodbye" />
</React.StrictMode>,
);
});
expect(container.textContent).toBe('GOODBYE');
expect(log).toEqual([
'Compute toUpperCase: GOODBYE',
'Compute toUpperCase: GOODBYE',
{uppercased: 'GOODBYE'},
{uppercased: 'GOODBYE'},
]);
expect(log[2]).toBe(log[3]);
});
it('double invokes setState updater functions', async () => {
const log = [];
let setCount;
function App() {
const [count, _setCount] = useState(0);
setCount = _setCount;
return count;
}
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
});
expect(container.textContent).toBe('0');
await act(() => {
setCount(() => {
log.push('Compute count: 1');
return 1;
});
});
expect(container.textContent).toBe('1');
expect(log).toEqual(['Compute count: 1', 'Compute count: 1']);
});
it('double invokes reducer functions', async () => {
const log = [];
function reducer(prevState, action) {
log.push('Compute new state: ' + action);
return action;
}
let dispatch;
function App() {
const [count, _dispatch] = useReducer(reducer, 0);
dispatch = _dispatch;
return count;
}
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
});
expect(container.textContent).toBe('0');
await act(() => {
dispatch(1);
});
expect(container.textContent).toBe('1');
expect(log).toEqual(['Compute new state: 1', 'Compute new state: 1']);
});
});
describe('Concurrent Mode', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
act = require('internal-test-utils').act;
});
it('should warn about unsafe legacy lifecycle methods anywhere in a StrictMode tree', async () => {
function StrictRoot() {
return (
<React.StrictMode>
<App />
</React.StrictMode>
);
}
class App extends React.Component {
UNSAFE_componentWillMount() {}
UNSAFE_componentWillUpdate() {}
render() {
return (
<div>
<Wrapper>
<Foo />
</Wrapper>
<div>
<Bar />
<Foo />
</div>
</div>
);
}
}
function Wrapper({children}) {
return <div>{children}</div>;
}
class Foo extends React.Component {
UNSAFE_componentWillReceiveProps() {}
render() {
return null;
}
}
class Bar extends React.Component {
UNSAFE_componentWillReceiveProps() {}
render() {
return null;
}
}
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(
async () => await act(() => root.render(<StrictRoot />)),
).toErrorDev(
[
`Warning: Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. See https://reactjs.org/link/unsafe-component-lifecycles for details.
* Move code with side effects to componentDidMount, and set initial state in the constructor.
Please update the following components: App`,
`Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://reactjs.org/link/unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state
Please update the following components: Bar, Foo`,
`Warning: Using UNSAFE_componentWillUpdate in strict mode is not recommended and may indicate bugs in your code. See https://reactjs.org/link/unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
Please update the following components: App`,
],
{withoutStack: true},
);
await act(() => root.render(<App />));
});
it('should coalesce warnings by lifecycle name', async () => {
function StrictRoot() {
return (
<React.StrictMode>
<App />
</React.StrictMode>
);
}
class App extends React.Component {
UNSAFE_componentWillMount() {}
UNSAFE_componentWillUpdate() {}
render() {
return <Parent />;
}
}
class Parent extends React.Component {
componentWillMount() {}
componentWillUpdate() {}
componentWillReceiveProps() {}
render() {
return <Child />;
}
}
class Child extends React.Component {
UNSAFE_componentWillReceiveProps() {}
render() {
return null;
}
}
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(async () => {
await expect(
async () => await act(() => root.render(<StrictRoot />)),
).toErrorDev(
[
`Warning: Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. See https://reactjs.org/link/unsafe-component-lifecycles for details.
* Move code with side effects to componentDidMount, and set initial state in the constructor.
Please update the following components: App`,
`Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://reactjs.org/link/unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state
Please update the following components: Child`,
`Warning: Using UNSAFE_componentWillUpdate in strict mode is not recommended and may indicate bugs in your code. See https://reactjs.org/link/unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
Please update the following components: App`,
],
{withoutStack: true},
);
}).toWarnDev(
[
`Warning: componentWillMount has been renamed, and is not recommended for use. See https://reactjs.org/link/unsafe-component-lifecycles for details.
* Move code with side effects to componentDidMount, and set initial state in the constructor.
* Rename componentWillMount to UNSAFE_componentWillMount to suppress this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
Please update the following components: Parent`,
`Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See https://reactjs.org/link/unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state
* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
Please update the following components: Parent`,
`Warning: componentWillUpdate has been renamed, and is not recommended for use. See https://reactjs.org/link/unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
* Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
Please update the following components: Parent`,
],
{withoutStack: true},
);
await act(() => root.render(<StrictRoot />));
});
it('should warn about components not present during the initial render', async () => {
function StrictRoot({foo}) {
return <React.StrictMode>{foo ? <Foo /> : <Bar />}</React.StrictMode>;
}
class Foo extends React.Component {
UNSAFE_componentWillMount() {}
render() {
return null;
}
}
class Bar extends React.Component {
UNSAFE_componentWillMount() {}
render() {
return null;
}
}
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(async () => {
await act(() => root.render(<StrictRoot foo={true} />));
}).toErrorDev(
'Using UNSAFE_componentWillMount in strict mode is not recommended',
{withoutStack: true},
);
await expect(async () => {
await act(() => root.render(<StrictRoot foo={false} />));
}).toErrorDev(
'Using UNSAFE_componentWillMount in strict mode is not recommended',
{withoutStack: true},
);
await act(() => root.render(<StrictRoot foo={true} />));
await act(() => root.render(<StrictRoot foo={false} />));
});
it('should also warn inside of "strict" mode trees', () => {
const {StrictMode} = React;
class SyncRoot extends React.Component {
UNSAFE_componentWillMount() {}
UNSAFE_componentWillUpdate() {}
UNSAFE_componentWillReceiveProps() {}
render() {
return (
<StrictMode>
<Wrapper />
</StrictMode>
);
}
}
function Wrapper({children}) {
return (
<div>
<Bar />
<Foo />
</div>
);
}
class Foo extends React.Component {
UNSAFE_componentWillReceiveProps() {}
render() {
return null;
}
}
class Bar extends React.Component {
UNSAFE_componentWillReceiveProps() {}
render() {
return null;
}
}
const container = document.createElement('div');
expect(() => ReactDOM.render(<SyncRoot />, container)).toErrorDev(
'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
{withoutStack: true},
);
ReactDOM.render(<SyncRoot />, container);
});
});
describe('symbol checks', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
});
it('should switch from StrictMode to a Fragment and reset state', () => {
const {Fragment, StrictMode} = React;
function ParentComponent({useFragment}) {
return useFragment ? (
<Fragment>
<ChildComponent />
</Fragment>
) : (
<StrictMode>
<ChildComponent />
</StrictMode>
);
}
class ChildComponent extends React.Component {
state = {
count: 0,
};
static getDerivedStateFromProps(nextProps, prevState) {
return {
count: prevState.count + 1,
};
}
render() {
return `count:${this.state.count}`;
}
}
const container = document.createElement('div');
ReactDOM.render(<ParentComponent useFragment={false} />, container);
expect(container.textContent).toBe('count:1');
ReactDOM.render(<ParentComponent useFragment={true} />, container);
expect(container.textContent).toBe('count:1');
});
it('should switch from a Fragment to StrictMode and reset state', () => {
const {Fragment, StrictMode} = React;
function ParentComponent({useFragment}) {
return useFragment ? (
<Fragment>
<ChildComponent />
</Fragment>
) : (
<StrictMode>
<ChildComponent />
</StrictMode>
);
}
class ChildComponent extends React.Component {
state = {
count: 0,
};
static getDerivedStateFromProps(nextProps, prevState) {
return {
count: prevState.count + 1,
};
}
render() {
return `count:${this.state.count}`;
}
}
const container = document.createElement('div');
ReactDOM.render(<ParentComponent useFragment={true} />, container);
expect(container.textContent).toBe('count:1');
ReactDOM.render(<ParentComponent useFragment={false} />, container);
expect(container.textContent).toBe('count:1');
});
it('should update with StrictMode without losing state', () => {
const {StrictMode} = React;
function ParentComponent() {
return (
<StrictMode>
<ChildComponent />
</StrictMode>
);
}
class ChildComponent extends React.Component {
state = {
count: 0,
};
static getDerivedStateFromProps(nextProps, prevState) {
return {
count: prevState.count + 1,
};
}
render() {
return `count:${this.state.count}`;
}
}
const container = document.createElement('div');
ReactDOM.render(<ParentComponent />, container);
expect(container.textContent).toBe('count:1');
ReactDOM.render(<ParentComponent />, container);
expect(container.textContent).toBe('count:2');
});
});
describe('string refs', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
});
it('should warn within a strict tree', () => {
const {StrictMode} = React;
class OuterComponent extends React.Component {
render() {
return (
<StrictMode>
<InnerComponent ref="somestring" />
</StrictMode>
);
}
}
class InnerComponent extends React.Component {
render() {
return null;
}
}
const container = document.createElement('div');
expect(() => {
ReactDOM.render(<OuterComponent />, container);
}).toErrorDev(
'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 OuterComponent (at **)',
);
ReactDOM.render(<OuterComponent />, container);
});
it('should warn within a strict tree', () => {
const {StrictMode} = React;
class OuterComponent extends React.Component {
render() {
return (
<StrictMode>
<InnerComponent />
</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);
}).toErrorDev(
'Warning: Component "InnerComponent" 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 **)',
);
ReactDOM.render(<OuterComponent />, container);
});
});
describe('context legacy', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
PropTypes = require('prop-types');
});
afterEach(() => {
jest.restoreAllMocks();
});
it('should warn if the legacy context API have been used in strict mode', () => {
class LegacyContextProvider extends React.Component {
getChildContext() {
return {color: 'purple'};
}
render() {
return (
<div>
<LegacyContextConsumer />
<FunctionalLegacyContextConsumer />
</div>
);
}
}
function FunctionalLegacyContextConsumer() {
return null;
}
LegacyContextProvider.childContextTypes = {
color: PropTypes.string,
};
class LegacyContextConsumer extends React.Component {
render() {
return null;
}
}
const {StrictMode} = React;
class Root extends React.Component {
render() {
return (
<div>
<StrictMode>
<LegacyContextProvider />
</StrictMode>
</div>
);
}
}
LegacyContextConsumer.contextTypes = {
color: PropTypes.string,
};
FunctionalLegacyContextConsumer.contextTypes = {
color: PropTypes.string,
};
const container = document.createElement('div');
expect(() => {
ReactDOM.render(<Root />, container);
}).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 ' +
'using it should migrate to the new version.' +
'\n\nPlease update the following components: ' +
'FunctionalLegacyContextConsumer, LegacyContextConsumer, LegacyContextProvider' +
'\n\nLearn more about this warning here: ' +
'https://reactjs.org/link/legacy-context' +
'\n in LegacyContextProvider (at **)' +
'\n in div (at **)' +
'\n in Root (at **)',
);
ReactDOM.render(<Root />, container);
});
describe('console logs logging', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
});
if (ReactFeatureFlags.consoleManagedByDevToolsDuringStrictMode) {
it('does not disable logs for class double render', () => {
spyOnDevAndProd(console, 'log');
let count = 0;
class Foo extends React.Component {
render() {
count++;
console.log('foo ' + count);
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledWith('foo 1');
});
it('does not disable logs for class double ctor', () => {
spyOnDevAndProd(console, 'log');
let count = 0;
class Foo extends React.Component {
constructor(props) {
super(props);
count++;
console.log('foo ' + count);
}
render() {
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledWith('foo 1');
});
it('does not disable logs for class double getDerivedStateFromProps', () => {
spyOnDevAndProd(console, 'log');
let count = 0;
class Foo extends React.Component {
state = {};
static getDerivedStateFromProps() {
count++;
console.log('foo ' + count);
return {};
}
render() {
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledWith('foo 1');
});
it('does not disable logs for class double shouldComponentUpdate', () => {
spyOnDevAndProd(console, 'log');
let count = 0;
class Foo extends React.Component {
state = {};
shouldComponentUpdate() {
count++;
console.log('foo ' + count);
return {};
}
render() {
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledWith('foo 1');
});
it('does not disable logs for class state updaters', () => {
spyOnDevAndProd(console, 'log');
let inst;
let count = 0;
class Foo extends React.Component {
state = {};
render() {
inst = this;
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
inst.setState(() => {
count++;
console.log('foo ' + count);
return {};
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledWith('foo 1');
});
it('does not disable logs for function double render', () => {
spyOnDevAndProd(console, 'log');
let count = 0;
function Foo() {
count++;
console.log('foo ' + count);
return null;
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledWith('foo 1');
});
} else {
it('disable logs for class double render', () => {
spyOnDevAndProd(console, 'log');
let count = 0;
class Foo extends React.Component {
render() {
count++;
console.log('foo ' + count);
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('foo 1');
});
it('disables logs for class double ctor', () => {
spyOnDevAndProd(console, 'log');
let count = 0;
class Foo extends React.Component {
constructor(props) {
super(props);
count++;
console.log('foo ' + count);
}
render() {
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('foo 1');
});
it('disable logs for class double getDerivedStateFromProps', () => {
spyOnDevAndProd(console, 'log');
let count = 0;
class Foo extends React.Component {
state = {};
static getDerivedStateFromProps() {
count++;
console.log('foo ' + count);
return {};
}
render() {
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('foo 1');
});
it('disable logs for class double shouldComponentUpdate', () => {
spyOnDevAndProd(console, 'log');
let count = 0;
class Foo extends React.Component {
state = {};
shouldComponentUpdate() {
count++;
console.log('foo ' + count);
return {};
}
render() {
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('foo 1');
});
it('disable logs for class state updaters', () => {
spyOnDevAndProd(console, 'log');
let inst;
let count = 0;
class Foo extends React.Component {
state = {};
render() {
inst = this;
return null;
}
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
inst.setState(() => {
count++;
console.log('foo ' + count);
return {};
});
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('foo 1');
});
it('disable logs for function double render', () => {
spyOnDevAndProd(console, 'log');
let count = 0;
function Foo() {
count++;
console.log('foo ' + count);
return null;
}
const container = document.createElement('div');
ReactDOM.render(
<React.StrictMode>
<Foo />
</React.StrictMode>,
container,
);
expect(count).toBe(__DEV__ ? 2 : 1);
expect(console.log).toBeCalledTimes(1);
expect(console.log).toBeCalledWith('foo 1');
});
}
});
});