diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
index 54649cc28f224..10f63f6ac1fa3 100644
--- a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
@@ -244,10 +244,11 @@ describe('ReactCompositeComponent', () => {
ReactDOM.unmountComponentAtNode(container);
expect(() => instance.forceUpdate()).toWarnDev(
- 'Can only update a mounted or mounting component. This usually means ' +
- 'you called setState, replaceState, or forceUpdate on an unmounted ' +
- 'component. This is a no-op.\n\nPlease check the code for the ' +
- 'Component component.',
+ "Warning: Can't call setState (or forceUpdate) on an unmounted " +
+ 'component. This is a no-op, but it indicates a memory leak in your ' +
+ 'application. To fix, cancel all subscriptions and asynchronous ' +
+ 'tasks in the componentWillUnmount method.\n' +
+ ' in Component (at **)',
);
// No additional warning should be recorded
@@ -269,10 +270,15 @@ describe('ReactCompositeComponent', () => {
}
}
- let instance = ;
- expect(instance.setState).not.toBeDefined();
-
- instance = ReactDOM.render(instance, container);
+ let instance;
+ ReactDOM.render(
+
+
+ (instance = c || instance)} />
+
+
,
+ container,
+ );
expect(renders).toBe(1);
@@ -280,15 +286,17 @@ describe('ReactCompositeComponent', () => {
expect(renders).toBe(2);
- ReactDOM.unmountComponentAtNode(container);
+ ReactDOM.render(, container);
expect(() => {
instance.setState({value: 2});
}).toWarnDev(
- 'Can only update a mounted or mounting component. This usually means ' +
- 'you called setState, replaceState, or forceUpdate on an unmounted ' +
- 'component. This is a no-op.\n\nPlease check the code for the ' +
- 'Component component.',
+ "Warning: Can't call setState (or forceUpdate) on an unmounted " +
+ 'component. This is a no-op, but it indicates a memory leak in your ' +
+ 'application. To fix, cancel all subscriptions and asynchronous ' +
+ 'tasks in the componentWillUnmount method.\n' +
+ ' in Component (at **)\n' +
+ ' in span',
);
expect(renders).toBe(2);
diff --git a/packages/react-reconciler/src/ReactFiberScheduler.js b/packages/react-reconciler/src/ReactFiberScheduler.js
index 1aaa20a4c03f7..dcd5dcd76f243 100644
--- a/packages/react-reconciler/src/ReactFiberScheduler.js
+++ b/packages/react-reconciler/src/ReactFiberScheduler.js
@@ -14,6 +14,7 @@ import type {HydrationContext} from './ReactFiberHydrationContext';
import type {ExpirationTime} from './ReactFiberExpirationTime';
import ReactErrorUtils from 'shared/ReactErrorUtils';
+import {getStackAddendumByWorkInProgressFiber} from 'shared/ReactFiberComponentTreeHook';
import {ReactCurrentOwner} from 'shared/ReactGlobalSharedState';
import ReactStrictModeWarnings from './ReactStrictModeWarnings';
import {
@@ -112,17 +113,19 @@ if (__DEV__) {
const didWarnStateUpdateForUnmountedComponent = {};
warnAboutUpdateOnUnmounted = function(fiber: Fiber) {
+ // We show the whole stack but dedupe on the top component's name because
+ // the problematic code almost always lies inside that component.
const componentName = getComponentName(fiber) || 'ReactClass';
if (didWarnStateUpdateForUnmountedComponent[componentName]) {
return;
}
warning(
false,
- 'Can only update a mounted or mounting ' +
- 'component. This usually means you called setState, replaceState, ' +
- 'or forceUpdate on an unmounted component. This is a no-op.\n\nPlease ' +
- 'check the code for the %s component.',
- componentName,
+ "Can't call setState (or forceUpdate) on an unmounted component. This " +
+ 'is a no-op, but it indicates a memory leak in your application. To ' +
+ 'fix, cancel all subscriptions and asynchronous tasks in the ' +
+ 'componentWillUnmount method.%s',
+ getStackAddendumByWorkInProgressFiber(fiber),
);
didWarnStateUpdateForUnmountedComponent[componentName] = true;
};