Skip to content

Commit 3a0b539

Browse files
committed
Tweak not-yet-mounted setState warning
1 parent eb6e752 commit 3a0b539

File tree

2 files changed

+87
-4
lines changed

2 files changed

+87
-4
lines changed

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,86 @@ 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. ' +
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. ' +
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 silently allow `setState`, not call cb on unmounting components', () => {
281+
let cbCalled = false;
282+
const container = document.createElement('div');
283+
document.body.appendChild(container);
284+
285+
class Component extends React.Component {
286+
state = {value: 0};
287+
288+
componentWillUnmount() {
289+
expect(() => {
290+
this.setState({value: 2}, function() {
291+
cbCalled = true;
292+
});
293+
}).not.toThrow();
294+
}
295+
296+
render() {
297+
return <div />;
298+
}
299+
}
300+
301+
const instance = ReactDOM.render(<Component />, container);
302+
instance.setState({value: 1});
303+
304+
ReactDOM.unmountComponentAtNode(container);
305+
expect(cbCalled).toBe(false);
306+
});
307+
228308
it('should warn about `forceUpdate` on unmounted components', () => {
229309
const container = document.createElement('div');
230310
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. ' +
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)