diff --git a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
index 505bb14bf04c1..5c8488417d33a 100644
--- a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
+++ b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
@@ -214,10 +214,12 @@ describe('ReactComponentLifeCycle', () => {
expect(() => {
ReactTestUtils.renderIntoDocument();
}).toWarnDev(
- 'Warning: setState(...): Can only update a mounted or ' +
- 'mounting component. This usually means you called setState() on an ' +
- 'unmounted component. This is a no-op.\n\nPlease check the code for the ' +
- 'StatefulComponent component.',
+ "Warning: Can't call setState on a component that is not yet mounted. " +
+ 'This is a no-op, but it might indicate a bug in your application.\n\n' +
+ 'To fix, assign the initial state in the StatefulComponent constructor. ' +
+ 'If the state needs to reflect an external data source, ' +
+ 'you may also add a componentDidMount lifecycle hook to StatefulComponent ' +
+ 'and call setState there if the external data has changed.',
);
// Check deduplication; (no extra warnings should be logged).
diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
index 10f63f6ac1fa3..4b8b996467914 100644
--- a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
@@ -225,6 +225,58 @@ describe('ReactCompositeComponent', () => {
expect(inputProps.prop).not.toBeDefined();
});
+ it('should warn about `forceUpdate` on not-yet-mounted components', () => {
+ class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.forceUpdate();
+ }
+ render() {
+ return
;
+ }
+ }
+
+ const container = document.createElement('div');
+ expect(() => ReactDOM.render(, container)).toWarnDev(
+ "Warning: Can't call forceUpdate on a component that is not yet mounted. " +
+ 'This is a no-op, but it might indicate a bug in your application.\n\n' +
+ 'To fix, assign the initial state in the MyComponent constructor. ' +
+ 'If the state needs to reflect an external data source, ' +
+ 'you may also add a componentDidMount lifecycle hook to MyComponent ' +
+ 'and call setState there if the external data has changed.',
+ );
+
+ // No additional warning should be recorded
+ const container2 = document.createElement('div');
+ ReactDOM.render(, container2);
+ });
+
+ it('should warn about `setState` on not-yet-mounted components', () => {
+ class MyComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ this.setState();
+ }
+ render() {
+ return ;
+ }
+ }
+
+ const container = document.createElement('div');
+ expect(() => ReactDOM.render(, container)).toWarnDev(
+ "Warning: Can't call setState on a component that is not yet mounted. " +
+ 'This is a no-op, but it might indicate a bug in your application.\n\n' +
+ 'To fix, assign the initial state in the MyComponent constructor. ' +
+ 'If the state needs to reflect an external data source, ' +
+ 'you may also add a componentDidMount lifecycle hook to MyComponent ' +
+ 'and call setState there if the external data has changed.',
+ );
+
+ // No additional warning should be recorded
+ const container2 = document.createElement('div');
+ ReactDOM.render(, container2);
+ });
+
it('should warn about `forceUpdate` on unmounted components', () => {
const container = document.createElement('div');
document.body.appendChild(container);
diff --git a/packages/react/src/ReactNoopUpdateQueue.js b/packages/react/src/ReactNoopUpdateQueue.js
index 5aa1ccf91589d..3b18c5b8b9b9f 100644
--- a/packages/react/src/ReactNoopUpdateQueue.js
+++ b/packages/react/src/ReactNoopUpdateQueue.js
@@ -21,12 +21,15 @@ function warnNoop(publicInstance, callerName) {
}
warning(
false,
- '%s(...): Can only update a mounted or mounting component. ' +
- 'This usually means you called %s() on an unmounted component. ' +
- 'This is a no-op.\n\nPlease check the code for the %s component.',
- callerName,
+ "Can't call %s on a component that is not yet mounted. " +
+ 'This is a no-op, but it might indicate a bug in your application.\n\n' +
+ 'To fix, assign the initial state in the %s constructor. ' +
+ 'If the state needs to reflect an external data source, ' +
+ 'you may also add a componentDidMount lifecycle hook to %s ' +
+ 'and call setState there if the external data has changed.',
callerName,
componentName,
+ componentName,
);
didWarnStateUpdateForUnmountedComponent[warningKey] = true;
}