diff --git a/src/renderers/shared/reconciler/ReactCompositeComponent.js b/src/renderers/shared/reconciler/ReactCompositeComponent.js index 65b28c6c773cf..9acf732045c26 100644 --- a/src/renderers/shared/reconciler/ReactCompositeComponent.js +++ b/src/renderers/shared/reconciler/ReactCompositeComponent.js @@ -119,6 +119,7 @@ var ReactCompositeComponentMixin = { this._pendingStateQueue = null; this._pendingReplaceState = false; this._pendingForceUpdate = false; + this._processingChildContext = false; this._renderedNodeType = null; this._renderedComponent = null; @@ -496,7 +497,9 @@ var ReactCompositeComponentMixin = { _processChildContext: function(currentContext) { var Component = this._currentElement.type; var inst = this._instance; + this._processingChildContext = true; var childContext = inst.getChildContext && inst.getChildContext(); + this._processingChildContext = false; if (childContext) { invariant( typeof Component.childContextTypes === 'object', diff --git a/src/renderers/shared/reconciler/ReactUpdateQueue.js b/src/renderers/shared/reconciler/ReactUpdateQueue.js index d2e7cadb28cf6..939b712bcc4c7 100644 --- a/src/renderers/shared/reconciler/ReactUpdateQueue.js +++ b/src/renderers/shared/reconciler/ReactUpdateQueue.js @@ -43,6 +43,12 @@ function getInternalInstanceReadyForUpdate(publicInstance, callerName) { } if (__DEV__) { + warning( + !internalInstance._processingChildContext, + '%s(...): Cannot update during processing child context.', + callerName + ); + warning( ReactCurrentOwner.current == null, '%s(...): Cannot update during an existing state transition (such as ' + diff --git a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js index 456b01325851f..d8b588d0d75e4 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js @@ -469,6 +469,32 @@ describe('ReactCompositeComponent', function() { expect(innerUnmounted).toBe(true); }); + it('should warn when setState() called within getChildContext()', function() { + var Component = React.createClass({ + childContextTypes: { + bogus: ReactPropTypes.bool.isRequired, + }, + + getChildContext: function() { + this.setState({a: 1}); + return {bogus: false}; + }, + + render: function() { + return
; + }, + }); + + expect(() => { + ReactTestUtils.renderIntoDocument(); + }).toThrow('Maximum call stack size exceeded'); + + expect(console.error.argsForCall.length).toBeGreaterThan(0); + expect(console.error.argsForCall[0][0]).toBe( + 'Warning: setState(...): Cannot update during processing child context.' + ); + }); + it('should warn when shouldComponentUpdate() returns undefined', function() { var Component = React.createClass({ getInitialState: function() {