Change:
expect(aUpdated).toBe(true); }); <ins> it('should flush updates in the correct order', function() { var updates = []; var Outer = React.createClass({ getInitialState: function() { return {x: 0}; }, render: function() { updates.push('Outer-render-' + this.state.x); return <div><Inner x={this.state.x} ref="inner" /></div>; }, componentDidUpdate: function() { var x = this.state.x; updates.push('Outer-didUpdate-' + x); updates.push('Inner-setState-' + x); this.refs.inner.setState({x: x}, function() { updates.push('Inner-callback-' + x); }); } }); var Inner = React.createClass({ getInitialState: function() { return {x: 0}; }, render: function() { updates.push('Inner-render-' + this.props.x + '-' + this.state.x); return <div />; }, componentDidUpdate: function() { updates.push('Inner-didUpdate-' + this.props.x + '-' + this.state.x); } }); var instance = ReactTestUtils.renderIntoDocument(<Outer />); updates.push('Outer-setState-1'); instance.setState({x: 1}, function() { updates.push('Outer-callback-1'); updates.push('Outer-setState-2'); instance.setState({x: 2}, function() { updates.push('Outer-callback-2'); }); }); expect(updates).toEqual([ 'Outer-render-0', 'Inner-render-0-0', 'Outer-setState-1', 'Outer-render-1', 'Inner-render-1-0', 'Inner-didUpdate-1-0', 'Outer-didUpdate-1', 'Inner-setState-1', 'Inner-render-1-1', 'Inner-didUpdate-1-1', 'Inner-callback-1', 'Outer-callback-1', 'Outer-setState-2', 'Outer-render-2', 'Inner-render-2-1', 'Inner-didUpdate-2-1', 'Outer-didUpdate-2', 'Inner-setState-2', 'Inner-render-2-2', 'Inner-didUpdate-2-2', 'Inner-callback-2', 'Outer-callback-2' ]); }); it('should queue nested updates', function() { // See https://github.com/facebook/react/issues/1147 var X = React.createClass({ getInitialState: function() { return {s: 0}; }, render: function() { if (this.state.s === 0) { return <div> <span>0</span> </div>; } else { return <div>1</div>; } }, go: function() { this.setState({s: 1}); this.setState({s: 0}); this.setState({s: 1}); } }); var Y = React.createClass({ render: function() { return <div> <Z /> </div>; } }); var Z = React.createClass({ render: function() { return <div />; }, componentWillUpdate: function() { x.go(); } }); var x; var y; x = ReactTestUtils.renderIntoDocument(<X />); y = ReactTestUtils.renderIntoDocument(<Y />); expect(x.getDOMNode().textContent).toBe('0'); y.forceUpdate(); expect(x.getDOMNode().textContent).toBe('1'); }); it('should queue updates from during mount', function() { // See https://github.com/facebook/react/issues/1353 var a; var A = React.createClass({ getInitialState: function() { return {x: 0}; }, componentWillMount: function() { a = this; }, render: function() { return <div>A{this.state.x}</div>; } }); var B = React.createClass({ componentWillMount: function() { a.setState({x: 1}); }, render: function() { return <div />; } }); ReactUpdates.batchedUpdates(function() { ReactTestUtils.renderIntoDocument( <div> <A /> <B /> </div> ); }); expect(a.state.x).toBe(1); expect(a.getDOMNode().textContent).toBe('A1'); }); it('calls componentWillReceiveProps setState callback properly', function() { var callbackCount = 0; var A = React.createClass({ getInitialState: function() { return {x: this.props.x}; }, componentWillReceiveProps: function(nextProps) { var newX = nextProps.x; this.setState({x: newX}, function() { // State should have updated by the time this callback gets called expect(this.state.x).toBe(newX); callbackCount++; }); }, render: function() { return <div>{this.state.x}</div>; } }); var container = document.createElement('div'); React.renderComponent(<A x={1} />, container); React.renderComponent(<A x={2} />, container); expect(callbackCount).toBe(1); }); </ins> });

Discussion:
This throws . Maybe this can be simplified some more but this was the simplest repro I could make. Thanks for sending a repro case over.
Wow, it really was a bug. Narrowed it down a little, but it's still rediculously "complex". Big props to for repro! Anyway, just glancing at the code and from experience, I can safely assume that the issue is that receives multiple updates to the same node from . Which intuitively didn't make any sense at all to me, since always calls when it's done and is basically just recursive. However, when taking another peek at the code, I'm pretty sure that the issue is that (in this case) makes the invalid assumption that all updates belongs to the same root. When that is not the case, an to a component which itself updates another root will only once everything is done. The updates to the other root will NOT after each on that root because will always greater than 0. In practice this means that whenever updates another root, all DOM updates on that root will be batched one after the other (as opposed to batching component updates) which is a violation of assumptions in . It's also very inefficient. Intuitively it seems that what should happen is that all component updates within a call should be (but that is not for sure), exactly how this should be solved in practice I'm not 100% sure right now. But I'm 95% sure that this is what's happening, and the repro does have all the ingredients.
Speaking a bit to in chat, I feel like the issue is that and when called from within become asynchronous as opposed to synchronous as they usually are. Which leads to this issue, but also possibly a lot of other subtle issues.
Apparently is not at all to blame...
Narrowed it down even further, apparently it breaks immediately on the first , the error just didn't show up until later unless prodded.

I still don't feel any more enlightened on the issue though...
Your latest two jsbins (4 & 6) throw different error for me (from in X ): And, if one removes Z from A , the same X throws , because then A render won't have any refs on that render so will be undefined... React docs warn that "Never access refs inside of any component's render method - or while any component's render method is even running anywhere in the call stack.", don't know if it's relevant here.
Yeah different error, but I'm sure the cause is the same. The issue is the span not actually ever being rendered, just that in the old repro it doesn't actually turn into an error until later.
Ok, so perhaps here's a reduced test case, see console: is being called twice in a row, and the first time the span is not there in DOM even though it has been rendered already. One culprit for this behavior may be that you are effectively calling setState on sibling component where setState would not be allowed on component itself (triggered via componentWillUpdate).
Huh? It does end up being rendered in your test case, and there's no error?
no I didn't want to throw the error so that you can see that the componentDidUpdate will be called double. The point is that first the span is not there and if one would try to access it (as in your ref.getDOMNode()) it would throw. See console for flow.
is defined so it's an error regardless, and more than that, React expects the node to be there, but it isn't and throws an error. Even after it still isn't physically there in the DOM. So something is wrong. Unless I'm missing something.
Yes, there is definitely a problem there as you said, sorry for not being clear. I just wanted to reduce it so that there are no refs or forceUpdates which may bring other problems. One could throw the error in componentDidUpdate for clarity, as the innerHTML is definitely empty where it should not be. But it is interesting that it will be called right away again, and then the span will be there.

But it throws:
Was this fixed?
Was clearing up old some old issues and it seemed like this was fixed, but apparently not.
I've found this same problem. Here's a minimal failing example for React 0.12.2: ,output However, it appears that this is fixed in 0.13RC: ,output. Any ideas which commit fixed this? If there aren't tests which exercise this it might creep back in.
Same here. Was perhaps related? I created a simple test case based on jsbin, which currently passes. Unfortunately I'm unable to run tests in anything besides master so can't really validate.
AFAIK no longer exists as of React 0.13, so this issue should be irrelevant now.