@@ -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 ) ;
0 commit comments