diff --git a/src/renderers/shared/reconciler/ReactUpdateQueue.js b/src/renderers/shared/reconciler/ReactUpdateQueue.js index d293bc9bc3..73850a7cd5 100644 --- a/src/renderers/shared/reconciler/ReactUpdateQueue.js +++ b/src/renderers/shared/reconciler/ReactUpdateQueue.js @@ -32,7 +32,7 @@ function enqueueUpdate(internalInstance) { } function getInternalInstanceReadyForUpdate(publicInstance, callerName) { - invariant( + warning( ReactCurrentOwner.current == null, '%s(...): Cannot update during an existing state transition ' + '(such as within `render`). Render methods should be a pure function ' + diff --git a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js index c23ea8c98f..1565aba035 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js @@ -346,6 +346,49 @@ describe('ReactCompositeComponent', function() { expect(cbCalled).toBe(false); }); + + it('should warn about `setState` in render', function() { + var container = document.createElement('div'); + + var renderedState = -1; + + var Component = React.createClass({ + getInitialState: function() { + return {value: 0}; + }, + render: function() { + renderedState = this.state.value; + if (this.state.value === 0) { + this.setState({ value: 1 }); + } + return
; + }, + }); + + expect(console.error.calls.length).toBe(0); + + var instance = React.render(, container); + + expect(console.error.calls.length).toBe(1); + expect(console.error.argsForCall[0][0]).toBe( + 'Warning: setState(...): Cannot update during an existing state ' + + 'transition (such as within `render`). Render methods should be a pure ' + + 'function of props and state.' + ); + + // The setState call is queued but the result isn't immediately available + // during this pass. It is queued up but not flushed. The behavior is more + // or less undefined. + expect(renderedState).toBe(0); + expect(instance.state.value).toBe(0); + + // Forcing a rerender anywhere will cause the update to happen. + var instance2 = React.render(, container); + expect(instance).toBe(instance2); + expect(renderedState).toBe(1); + expect(instance2.state.value).toBe(1); + }); + it('should not allow `setProps` on unmounted components', function() { var container = document.createElement('div'); document.body.appendChild(container);