@samitouri / QOS-React-2 / commits / 5855e9f215

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.

Sophie Alpert committed Mar 29, 2018 at 08:21 UTC 5855e9f2158b31d945f3fcc5bc582389dbecc88e
2 files changed +29 -18
packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+21 -13
@@ -244,10 +244,11 @@ describe('ReactCompositeComponent', () => {
244 ReactDOM.unmountComponentAtNode(container);
245
246 expect(() => instance.forceUpdate()).toWarnDev(
247 - 'Can only update a mounted or mounting component. This usually means ' +
248 - 'you called setState, replaceState, or forceUpdate on an unmounted ' +
249 - 'component. This is a no-op.\n\nPlease check the code for the ' +
250 - 'Component component.',
247 + "Warning: Can't call setState (or forceUpdate) on an unmounted " +
248 + 'component. This is a no-op, but it indicates a memory leak in your ' +
249 + 'application. To fix, cancel all subscriptions and asynchronous ' +
250 + 'tasks in the componentWillUnmount method.\n' +
251 + ' in Component (at **)',
252 );
253
254 // No additional warning should be recorded
@@ -269,10 +270,15 @@ describe('ReactCompositeComponent', () => {
270 }
271 }
272
272 - let instance = <Component />;
273 - expect(instance.setState).not.toBeDefined();
274 -
275 - instance = ReactDOM.render(instance, container);
273 + let instance;
274 + ReactDOM.render(
275 + <div>
276 + <span>
277 + <Component ref={c => (instance = c || instance)} />
278 + </span>
279 + </div>,
280 + container,
281 + );
282
283 expect(renders).toBe(1);
284
@@ -280,15 +286,17 @@ describe('ReactCompositeComponent', () => {
286
287 expect(renders).toBe(2);
288
283 - ReactDOM.unmountComponentAtNode(container);
289 + ReactDOM.render(<div />, container);
290
291 expect(() => {
292 instance.setState({value: 2});
293 }).toWarnDev(
288 - 'Can only update a mounted or mounting component. This usually means ' +
289 - 'you called setState, replaceState, or forceUpdate on an unmounted ' +
290 - 'component. This is a no-op.\n\nPlease check the code for the ' +
291 - 'Component component.',
294 + "Warning: Can't call setState (or forceUpdate) on an unmounted " +
295 + 'component. This is a no-op, but it indicates a memory leak in your ' +
296 + 'application. To fix, cancel all subscriptions and asynchronous ' +
297 + 'tasks in the componentWillUnmount method.\n' +
298 + ' in Component (at **)\n' +
299 + ' in span',
300 );
301
302 expect(renders).toBe(2);
packages/react-reconciler/src/ReactFiberScheduler.js
+8 -5
@@ -14,6 +14,7 @@ import type {HydrationContext} from './ReactFiberHydrationContext';
14 import type {ExpirationTime} from './ReactFiberExpirationTime';
15
16 import ReactErrorUtils from 'shared/ReactErrorUtils';
17 +import {getStackAddendumByWorkInProgressFiber} from 'shared/ReactFiberComponentTreeHook';
18 import {ReactCurrentOwner} from 'shared/ReactGlobalSharedState';
19 import ReactStrictModeWarnings from './ReactStrictModeWarnings';
20 import {
@@ -112,17 +113,19 @@ if (__DEV__) {
113 const didWarnStateUpdateForUnmountedComponent = {};
114
115 warnAboutUpdateOnUnmounted = function(fiber: Fiber) {
116 + // We show the whole stack but dedupe on the top component's name because
117 + // the problematic code almost always lies inside that component.
118 const componentName = getComponentName(fiber) || 'ReactClass';
119 if (didWarnStateUpdateForUnmountedComponent[componentName]) {
120 return;
121 }
122 warning(
123 false,
121 - 'Can only update a mounted or mounting ' +
122 - 'component. This usually means you called setState, replaceState, ' +
123 - 'or forceUpdate on an unmounted component. This is a no-op.\n\nPlease ' +
124 - 'check the code for the %s component.',
125 - componentName,
124 + "Can't call setState (or forceUpdate) on an unmounted component. This " +
125 + 'is a no-op, but it indicates a memory leak in your application. To ' +
126 + 'fix, cancel all subscriptions and asynchronous tasks in the ' +
127 + 'componentWillUnmount method.%s',
128 + getStackAddendumByWorkInProgressFiber(fiber),
129 );
130 didWarnStateUpdateForUnmountedComponent[componentName] = true;
131 };