Skip to content

Commit 36c2939

Browse files
authored
Improve not-yet-mounted setState warning (#12531)
* Tweak not-yet-mounted setState warning * Add \n\n
1 parent 0f2f90b commit 36c2939

File tree

3 files changed

+65
-8
lines changed

3 files changed

+65
-8
lines changed

packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,12 @@ describe('ReactComponentLifeCycle', () => {
214214
expect(() => {
215215
ReactTestUtils.renderIntoDocument(<StatefulComponent />);
216216
}).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.',
221223
);
222224

223225
// Check deduplication; (no extra warnings should be logged).

packages/react-dom/src/__tests__/ReactCompositeComponent-test.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,58 @@ describe('ReactCompositeComponent', () => {
225225
expect(inputProps.prop).not.toBeDefined();
226226
});
227227

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+
228280
it('should warn about `forceUpdate` on unmounted components', () => {
229281
const container = document.createElement('div');
230282
document.body.appendChild(container);

packages/react/src/ReactNoopUpdateQueue.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@ function warnNoop(publicInstance, callerName) {
2121
}
2222
warning(
2323
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.',
2830
callerName,
2931
componentName,
32+
componentName,
3033
);
3134
didWarnStateUpdateForUnmountedComponent[warningKey] = true;
3235
}

0 commit comments

Comments
 (0)