Improve not-yet-mounted setState warning (#12531)
* Tweak not-yet-mounted setState warning * Add \n\n
Dan Abramov committed
Apr 3, 2018 at 21:22 UTC
36c29393720157a3966ce1d50449a33a35bdf14c
3 files changed
+65
-8
packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
+6
-4
@@ -214,10 +214,12 @@ describe('ReactComponentLifeCycle', () => {
214
expect(() => {
215
ReactTestUtils.renderIntoDocument(<StatefulComponent />);
216
}).toWarnDev(
217
- 'Warning: setState(...): Can only update a mounted or ' +
218
- 'mounting component. This usually means you called setState() on an ' +
219
- 'unmounted component. This is a no-op.\n\nPlease check the code for the ' +
220
- 'StatefulComponent component.',
217
+ "Warning: Can't call setState on a component that is not yet mounted. " +
218
+ 'This is a no-op, but it might indicate a bug in your application.\n\n' +
219
+ 'To fix, assign the initial state in the StatefulComponent constructor. ' +
220
+ 'If the state needs to reflect an external data source, ' +
221
+ 'you may also add a componentDidMount lifecycle hook to StatefulComponent ' +
222
+ 'and call setState there if the external data has changed.',
223
);
224
225
// Check deduplication; (no extra warnings should be logged).
packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+52
@@ -225,6 +225,58 @@ describe('ReactCompositeComponent', () => {
225
expect(inputProps.prop).not.toBeDefined();
226
});
227
228
+ it('should warn about `forceUpdate` on not-yet-mounted components', () => {
229
+ class MyComponent extends React.Component {
230
+ constructor(props) {
231
+ super(props);
232
+ this.forceUpdate();
233
+ }
234
+ render() {
235
+ return <div />;
236
+ }
237
+ }
238
+
239
+ const container = document.createElement('div');
240
+ expect(() => ReactDOM.render(<MyComponent />, container)).toWarnDev(
241
+ "Warning: Can't call forceUpdate on a component that is not yet mounted. " +
242
+ 'This is a no-op, but it might indicate a bug in your application.\n\n' +
243
+ 'To fix, assign the initial state in the MyComponent constructor. ' +
244
+ 'If the state needs to reflect an external data source, ' +
245
+ 'you may also add a componentDidMount lifecycle hook to MyComponent ' +
246
+ 'and call setState there if the external data has changed.',
247
+ );
248
+
249
+ // No additional warning should be recorded
250
+ const container2 = document.createElement('div');
251
+ ReactDOM.render(<MyComponent />, container2);
252
+ });
253
+
254
+ it('should warn about `setState` on not-yet-mounted components', () => {
255
+ class MyComponent extends React.Component {
256
+ constructor(props) {
257
+ super(props);
258
+ this.setState();
259
+ }
260
+ render() {
261
+ return <div />;
262
+ }
263
+ }
264
+
265
+ const container = document.createElement('div');
266
+ expect(() => ReactDOM.render(<MyComponent />, container)).toWarnDev(
267
+ "Warning: Can't call setState on a component that is not yet mounted. " +
268
+ 'This is a no-op, but it might indicate a bug in your application.\n\n' +
269
+ 'To fix, assign the initial state in the MyComponent constructor. ' +
270
+ 'If the state needs to reflect an external data source, ' +
271
+ 'you may also add a componentDidMount lifecycle hook to MyComponent ' +
272
+ 'and call setState there if the external data has changed.',
273
+ );
274
+
275
+ // No additional warning should be recorded
276
+ const container2 = document.createElement('div');
277
+ ReactDOM.render(<MyComponent />, container2);
278
+ });
279
+
280
it('should warn about `forceUpdate` on unmounted components', () => {
281
const container = document.createElement('div');
282
document.body.appendChild(container);
packages/react/src/ReactNoopUpdateQueue.js
+7
-4
@@ -21,12 +21,15 @@ function warnNoop(publicInstance, callerName) {
21
}
22
warning(
23
false,
24
- '%s(...): Can only update a mounted or mounting component. ' +
25
- 'This usually means you called %s() on an unmounted component. ' +
26
- 'This is a no-op.\n\nPlease check the code for the %s component.',
27
- callerName,
24
+ "Can't call %s on a component that is not yet mounted. " +
25
+ 'This is a no-op, but it might indicate a bug in your application.\n\n' +
26
+ 'To fix, assign the initial state in the %s constructor. ' +
27
+ 'If the state needs to reflect an external data source, ' +
28
+ 'you may also add a componentDidMount lifecycle hook to %s ' +
29
+ 'and call setState there if the external data has changed.',
30
callerName,
31
componentName,
32
+ componentName,
33
);
34
didWarnStateUpdateForUnmountedComponent[warningKey] = true;
35
}