diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.new.js b/packages/react-reconciler/src/ReactFiberClassComponent.new.js
index c464b6c3bf411..3d10472c0441c 100644
--- a/packages/react-reconciler/src/ReactFiberClassComponent.new.js
+++ b/packages/react-reconciler/src/ReactFiberClassComponent.new.js
@@ -169,7 +169,7 @@ function applyDerivedStateFromProps(
nextProps: any,
) {
const prevState = workInProgress.memoizedState;
-
+ let partialState = getDerivedStateFromProps(nextProps, prevState);
if (__DEV__) {
if (
debugRenderPhaseSideEffectsForStrictMode &&
@@ -178,16 +178,11 @@ function applyDerivedStateFromProps(
disableLogs();
try {
// Invoke the function an extra time to help detect side-effects.
- getDerivedStateFromProps(nextProps, prevState);
+ partialState = getDerivedStateFromProps(nextProps, prevState);
} finally {
reenableLogs();
}
}
- }
-
- const partialState = getDerivedStateFromProps(nextProps, prevState);
-
- if (__DEV__) {
warnOnUndefinedDerivedState(ctor, partialState);
}
// Merge the partial state and the previous state.
@@ -323,6 +318,11 @@ function checkShouldComponentUpdate(
) {
const instance = workInProgress.stateNode;
if (typeof instance.shouldComponentUpdate === 'function') {
+ let shouldUpdate = instance.shouldComponentUpdate(
+ newProps,
+ newState,
+ nextContext,
+ );
if (__DEV__) {
if (
debugRenderPhaseSideEffectsForStrictMode &&
@@ -331,19 +331,15 @@ function checkShouldComponentUpdate(
disableLogs();
try {
// Invoke the function an extra time to help detect side-effects.
- instance.shouldComponentUpdate(newProps, newState, nextContext);
+ shouldUpdate = instance.shouldComponentUpdate(
+ newProps,
+ newState,
+ nextContext,
+ );
} finally {
reenableLogs();
}
}
- }
- const shouldUpdate = instance.shouldComponentUpdate(
- newProps,
- newState,
- nextContext,
- );
-
- if (__DEV__) {
if (shouldUpdate === undefined) {
console.error(
'%s.shouldComponentUpdate(): Returned undefined instead of a ' +
@@ -659,6 +655,7 @@ function constructClassInstance(
: emptyContextObject;
}
+ let instance = new ctor(props, context);
// Instantiate twice to help detect side-effects.
if (__DEV__) {
if (
@@ -667,14 +664,13 @@ function constructClassInstance(
) {
disableLogs();
try {
- new ctor(props, context); // eslint-disable-line no-new
+ instance = new ctor(props, context); // eslint-disable-line no-new
} finally {
reenableLogs();
}
}
}
- const instance = new ctor(props, context);
const state = (workInProgress.memoizedState =
instance.state !== null && instance.state !== undefined
? instance.state
diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.old.js b/packages/react-reconciler/src/ReactFiberClassComponent.old.js
index adb616c6262b9..ab80913c7b6b6 100644
--- a/packages/react-reconciler/src/ReactFiberClassComponent.old.js
+++ b/packages/react-reconciler/src/ReactFiberClassComponent.old.js
@@ -169,7 +169,7 @@ function applyDerivedStateFromProps(
nextProps: any,
) {
const prevState = workInProgress.memoizedState;
-
+ let partialState = getDerivedStateFromProps(nextProps, prevState);
if (__DEV__) {
if (
debugRenderPhaseSideEffectsForStrictMode &&
@@ -178,16 +178,11 @@ function applyDerivedStateFromProps(
disableLogs();
try {
// Invoke the function an extra time to help detect side-effects.
- getDerivedStateFromProps(nextProps, prevState);
+ partialState = getDerivedStateFromProps(nextProps, prevState);
} finally {
reenableLogs();
}
}
- }
-
- const partialState = getDerivedStateFromProps(nextProps, prevState);
-
- if (__DEV__) {
warnOnUndefinedDerivedState(ctor, partialState);
}
// Merge the partial state and the previous state.
@@ -323,6 +318,11 @@ function checkShouldComponentUpdate(
) {
const instance = workInProgress.stateNode;
if (typeof instance.shouldComponentUpdate === 'function') {
+ let shouldUpdate = instance.shouldComponentUpdate(
+ newProps,
+ newState,
+ nextContext,
+ );
if (__DEV__) {
if (
debugRenderPhaseSideEffectsForStrictMode &&
@@ -331,19 +331,15 @@ function checkShouldComponentUpdate(
disableLogs();
try {
// Invoke the function an extra time to help detect side-effects.
- instance.shouldComponentUpdate(newProps, newState, nextContext);
+ shouldUpdate = instance.shouldComponentUpdate(
+ newProps,
+ newState,
+ nextContext,
+ );
} finally {
reenableLogs();
}
}
- }
- const shouldUpdate = instance.shouldComponentUpdate(
- newProps,
- newState,
- nextContext,
- );
-
- if (__DEV__) {
if (shouldUpdate === undefined) {
console.error(
'%s.shouldComponentUpdate(): Returned undefined instead of a ' +
@@ -659,6 +655,7 @@ function constructClassInstance(
: emptyContextObject;
}
+ let instance = new ctor(props, context);
// Instantiate twice to help detect side-effects.
if (__DEV__) {
if (
@@ -667,14 +664,13 @@ function constructClassInstance(
) {
disableLogs();
try {
- new ctor(props, context); // eslint-disable-line no-new
+ instance = new ctor(props, context); // eslint-disable-line no-new
} finally {
reenableLogs();
}
}
}
- const instance = new ctor(props, context);
const state = (workInProgress.memoizedState =
instance.state !== null && instance.state !== undefined
? instance.state
diff --git a/packages/react/src/__tests__/ReactStrictMode-test.js b/packages/react/src/__tests__/ReactStrictMode-test.js
index a41f2ef2ccc75..5bd16f9c544dd 100644
--- a/packages/react/src/__tests__/ReactStrictMode-test.js
+++ b/packages/react/src/__tests__/ReactStrictMode-test.js
@@ -892,4 +892,196 @@ describe('context legacy', () => {
// Dedupe
ReactDOM.render(, container);
});
+
+ describe('disableLogs', () => {
+ it('disables logs once 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(
+
+
+ ,
+ container,
+ );
+
+ expect(count).toBe(__DEV__ ? 2 : 1);
+ expect(console.log).toBeCalledTimes(1);
+ // Note: we should display the first log because otherwise
+ // there is a risk of suppressing warnings when they happen,
+ // and on the next render they'd get deduplicated and ignored.
+ expect(console.log).toBeCalledWith('foo 1');
+ });
+
+ it('disables logs once 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(
+
+
+ ,
+ container,
+ );
+
+ expect(count).toBe(__DEV__ ? 2 : 1);
+ expect(console.log).toBeCalledTimes(1);
+ // Note: we should display the first log because otherwise
+ // there is a risk of suppressing warnings when they happen,
+ // and on the next render they'd get deduplicated and ignored.
+ expect(console.log).toBeCalledWith('foo 1');
+ });
+
+ it('disables logs once 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(
+
+
+ ,
+ container,
+ );
+
+ expect(count).toBe(__DEV__ ? 2 : 1);
+ expect(console.log).toBeCalledTimes(1);
+ // Note: we should display the first log because otherwise
+ // there is a risk of suppressing warnings when they happen,
+ // and on the next render they'd get deduplicated and ignored.
+ expect(console.log).toBeCalledWith('foo 1');
+ });
+
+ it('disables logs once 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(
+
+
+ ,
+ container,
+ );
+ // Trigger sCU:
+ ReactDOM.render(
+
+
+ ,
+ container,
+ );
+
+ expect(count).toBe(__DEV__ ? 2 : 1);
+ expect(console.log).toBeCalledTimes(1);
+ // Note: we should display the first log because otherwise
+ // there is a risk of suppressing warnings when they happen,
+ // and on the next render they'd get deduplicated and ignored.
+ expect(console.log).toBeCalledWith('foo 1');
+ });
+
+ it('disables logs once 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(
+
+
+ ,
+ container,
+ );
+ inst.setState(() => {
+ count++;
+ console.log('foo ' + count);
+ return {};
+ });
+
+ expect(count).toBe(__DEV__ ? 2 : 1);
+ expect(console.log).toBeCalledTimes(1);
+ // Note: we should display the first log because otherwise
+ // there is a risk of suppressing warnings when they happen,
+ // and on the next render they'd get deduplicated and ignored.
+ expect(console.log).toBeCalledWith('foo 1');
+ });
+
+ it('disables logs once 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(
+
+
+ ,
+ container,
+ );
+
+ expect(count).toBe(__DEV__ ? 2 : 1);
+ expect(console.log).toBeCalledTimes(1);
+ // Note: we should display the first log because otherwise
+ // there is a risk of suppressing warnings when they happen,
+ // and on the next render they'd get deduplicated and ignored.
+ expect(console.log).toBeCalledWith('foo 1');
+ });
+ });
});