@samitouri / QOS-React-2 / commits / 9c1916dedb

Fix #6114 - Calling setState inside getChildContext should warn

rainer oviir committed Mar 4, 2016 at 23:20 UTC 9c1916dedbe7e4462e8a4f43aad9b4e6bdbbf5ee
5 files changed +89
src/isomorphic/ReactDebugTool.js
+12
@@ -11,6 +11,7 @@
11
12 'use strict';
13
14 +var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
15 var warning = require('warning');
16
17 var eventHandlers = [];
@@ -48,6 +49,15 @@ var ReactDebugTool = {
49 }
50 }
51 },
52 + onBeginProcessingChildContext() {
53 + emitEvent('onBeginProcessingChildContext');
54 + },
55 + onEndProcessingChildContext() {
56 + emitEvent('onEndProcessingChildContext');
57 + },
58 + onSetState() {
59 + emitEvent('onSetState');
60 + },
61 onMountRootComponent(internalInstance) {
62 emitEvent('onMountRootComponent', internalInstance);
63 },
@@ -62,4 +72,6 @@ var ReactDebugTool = {
72 },
73 };
74
75 +ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
76 +
77 module.exports = ReactDebugTool;
src/isomorphic/devtools/ReactInvalidSetStateWarningDevTool.js new
+39
@@ -0,0 +1,39 @@
1 +/**
2 + * Copyright 2016-present, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @providesModule ReactInvalidSetStateWarningDevTool
10 + */
11 +
12 +'use strict';
13 +
14 +var warning = require('warning');
15 +
16 +if (__DEV__) {
17 + var processingChildContext = false;
18 +
19 + var warnInvalidSetState = function() {
20 + warning(
21 + !processingChildContext,
22 + 'setState(...): Cannot call setState() inside getChildContext()'
23 + );
24 + };
25 +}
26 +
27 +var ReactInvalidSetStateWarningDevTool = {
28 + onBeginProcessingChildContext() {
29 + processingChildContext = true;
30 + },
31 + onEndProcessingChildContext() {
32 + processingChildContext = false;
33 + },
34 + onSetState() {
35 + warnInvalidSetState();
36 + },
37 +};
38 +
39 +module.exports = ReactInvalidSetStateWarningDevTool;
src/isomorphic/modern/class/ReactComponent.js
+2
@@ -12,6 +12,7 @@
12 'use strict';
13
14 var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue');
15 +var ReactInstrumentation = require('ReactInstrumentation');
16
17 var canDefineProperty = require('canDefineProperty');
18 var emptyObject = require('emptyObject');
@@ -66,6 +67,7 @@ ReactComponent.prototype.setState = function(partialState, callback) {
67 'function which returns an object of state variables.'
68 );
69 if (__DEV__) {
70 + ReactInstrumentation.debugTool.onSetState();
71 warning(
72 partialState != null,
73 'setState(...): You passed an undefined or null state object; ' +
src/renderers/shared/reconciler/ReactCompositeComponent.js
+7
@@ -16,6 +16,7 @@ var ReactCurrentOwner = require('ReactCurrentOwner');
16 var ReactElement = require('ReactElement');
17 var ReactErrorUtils = require('ReactErrorUtils');
18 var ReactInstanceMap = require('ReactInstanceMap');
19 +var ReactInstrumentation = require('ReactInstrumentation');
20 var ReactNodeTypes = require('ReactNodeTypes');
21 var ReactPerf = require('ReactPerf');
22 var ReactPropTypeLocations = require('ReactPropTypeLocations');
@@ -496,7 +497,13 @@ var ReactCompositeComponentMixin = {
497 _processChildContext: function(currentContext) {
498 var Component = this._currentElement.type;
499 var inst = this._instance;
500 + if (__DEV__) {
501 + ReactInstrumentation.debugTool.onBeginProcessingChildContext();
502 + }
503 var childContext = inst.getChildContext && inst.getChildContext();
504 + if (__DEV__) {
505 + ReactInstrumentation.debugTool.onEndProcessingChildContext();
506 + }
507 if (childContext) {
508 invariant(
509 typeof Component.childContextTypes === 'object',
src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js
+29
@@ -424,6 +424,35 @@ describe('ReactCompositeComponent', function() {
424 expect(instance2.state.value).toBe(1);
425 });
426
427 + it('should warn about `setState` in getChildContext', function() {
428 + var container = document.createElement('div');
429 +
430 + var renderPasses = 0;
431 +
432 + var Component = React.createClass({
433 + getInitialState: function() {
434 + return {value: 0};
435 + },
436 + getChildContext: function() {
437 + if (this.state.value === 0) {
438 + this.setState({ value: 1 });
439 + }
440 + },
441 + render: function() {
442 + renderPasses++;
443 + return <div />;
444 + },
445 + });
446 + expect(console.error.calls.length).toBe(0);
447 + var instance = ReactDOM.render(<Component />, container);
448 + expect(renderPasses).toBe(2);
449 + expect(instance.state.value).toBe(1);
450 + expect(console.error.calls.length).toBe(1);
451 + expect(console.error.argsForCall[0][0]).toBe(
452 + 'Warning: setState(...): Cannot call setState() inside getChildContext()'
453 + );
454 + });
455 +
456 it('should cleanup even if render() fatals', function() {
457 var BadComponent = React.createClass({
458 render: function() {