Improve warning message for setState-on-unmounted (#12347)

This is one of the most common warnings people see, and I don't think the old text is especially clear. Improve it.
This commit is contained in:
Sophie Alpert
2018-03-29 16:21:22 +01:00
committed by Dan Abramov
parent 7a833dad95
commit 5855e9f215
2 changed files with 29 additions and 18 deletions
@@ -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 = <Component />;
expect(instance.setState).not.toBeDefined();
instance = ReactDOM.render(instance, container);
let instance;
ReactDOM.render(
<div>
<span>
<Component ref={c => (instance = c || instance)} />
</span>
</div>,
container,
);
expect(renders).toBe(1);
@@ -280,15 +286,17 @@ describe('ReactCompositeComponent', () => {
expect(renders).toBe(2);
ReactDOM.unmountComponentAtNode(container);
ReactDOM.render(<div />, 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);
+8 -5
View File
@@ -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;
};