Change:
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.

Discussion:
* Call the provided function in a context within which calls to `setState` * and friends are batched such that components aren't updated unnecessarily. */ <del> batchedUpdates: function(callback, param) { callback(param); </del> <ins> batchedUpdates: function(callback, a, b) { callback(a, b); </ins> } };

* Call the provided function in a context within which calls to `setState` * and friends are batched such that components aren't updated unnecessarily. */ <del> batchedUpdates: function(callback, param) { </del> <ins> batchedUpdates: function(callback, a, b) { </ins> var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; ReactDefaultBatchingStrategy.isBatchingUpdates = true; // The code is written this way to avoid extra allocations if (alreadyBatchingUpdates) { <del> callback(param); </del> <ins> callback(a, b); </ins> } else { <del> transaction.perform(callback, null, param); </del> <ins> transaction.perform(callback, null, a, b); </ins> } } };

"use strict"; <ins> var CallbackQueue = require('CallbackQueue'); var PooledClass = require('PooledClass'); </ins> var ReactCurrentOwner = require('ReactCurrentOwner'); var ReactPerf = require('ReactPerf'); <ins> var Transaction = require('Transaction'); </ins> var invariant = require('invariant'); <ins> var mixInto = require('mixInto'); </ins> var warning = require('warning'); var dirtyComponents = [];

); } <del> function batchedUpdates(callback, param) { </del> <ins> var NESTED_UPDATES = { initialize: function() { this.dirtyComponentsLength = dirtyComponents.length; }, close: function() { if (this.dirtyComponentsLength !== dirtyComponents.length) { // Additional updates were enqueued by componentDidUpdate handlers or // similar; before our own UPDATE_QUEUEING wrapper closes, we want to run // these new updates so that if A's componentDidUpdate calls setState on // B, B will update before the callback A's updater provided when calling // setState. dirtyComponents.splice(0, this.dirtyComponentsLength); flushBatchedUpdates(); } else { dirtyComponents.length = 0; } } }; var UPDATE_QUEUEING = { initialize: function() { this.callbackQueue.reset(); }, close: function() { this.callbackQueue.notifyAll(); } }; var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING]; function ReactUpdatesFlushTransaction() { this.reinitializeTransaction(); this.dirtyComponentsLength = null; this.callbackQueue = CallbackQueue.getPooled(null); this.reconcileTransaction = ReactUpdates.ReactReconcileTransaction.getPooled(); } mixInto(ReactUpdatesFlushTransaction, Transaction.Mixin); mixInto(ReactUpdatesFlushTransaction, { getTransactionWrappers: function() { return TRANSACTION_WRAPPERS; }, destructor: function() { this.dirtyComponentsLength = null; CallbackQueue.release(this.callbackQueue); this.callbackQueue = null; ReactUpdates.ReactReconcileTransaction.release(this.reconcileTransaction); this.reconcileTransaction = null; }, perform: function(method, scope, a) { // Essentially calls `this.reconcileTransaction.perform(method, scope, a)` // with this transaction's wrappers around it. return Transaction.Mixin.perform.call( this, this.reconcileTransaction.perform, this.reconcileTransaction, method, scope, a ); } }); PooledClass.addPoolingTo(ReactUpdatesFlushTransaction); function batchedUpdates(callback, a, b) { </ins> ensureInjected(); <del> batchingStrategy.batchedUpdates(callback, param); </del> <ins> batchingStrategy.batchedUpdates(callback, a, b); </ins> } /**

} function runBatchedUpdates(transaction) { <ins> var len = transaction.dirtyComponentsLength; invariant( len === dirtyComponents.length, 'Expected flush transaction's stored dirty-components length (%s) to ' + 'match dirty-components array length (%s).', len, dirtyComponents.length ); </ins> // Since reconciling a component higher in the owner hierarchy usually (not // always -- see shouldComponentUpdate()) will reconcile children, reconcile // them before their children by sorting the array. dirtyComponents.sort(mountDepthComparator); <del> for (var i = 0; i < dirtyComponents.length; i++) { </del> <ins> for (var i = 0; i < len; i++) { </ins> // If a component is unmounted before pending changes apply, ignore them // TODO: Queue unmounts in the same list to avoid this happening at all var component = dirtyComponents[i];

// stash the callbacks first var callbacks = component._pendingCallbacks; component._pendingCallbacks = null; <del> component.performUpdateIfNecessary(transaction); </del> <ins> component.performUpdateIfNecessary(transaction.reconcileTransaction); </ins> if (callbacks) { for (var j = 0; j < callbacks.length; j++) { <del> transaction.getReactMountReady().enqueue( </del> <ins> transaction.callbackQueue.enqueue( </ins> callbacks[j], component );

} } <del> function clearDirtyComponents() { dirtyComponents.length = 0; } function flushBatchedUpdatesOnce(transaction) { // Run these in separate functions so the JIT can optimize try { runBatchedUpdates(transaction); } finally { clearDirtyComponents(); } } </del> var flushBatchedUpdates = ReactPerf.measure( 'ReactUpdates', 'flushBatchedUpdates', function() { <del> // flushBatchedUpdatesOnce will clear the dirtyComponents array, but // mount-ready handlers (i.e., componentDidMount/Update) may enqueue more // state updates which we should apply immediately </del> <ins> // ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents // array and perform any updates enqueued by mount-ready handlers (i.e., // componentDidUpdate) but we need to check here too in order to catch // updates enqueued by setState callbacks. </ins> while (dirtyComponents.length) { <del> var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(); transaction.perform(flushBatchedUpdatesOnce, null, transaction); ReactUpdates.ReactReconcileTransaction.release(transaction); </del> <ins> var transaction = ReactUpdatesFlushTransaction.getPooled(); transaction.perform(runBatchedUpdates, null, transaction); ReactUpdatesFlushTransaction.release(transaction); </ins> } } );

); if (!batchingStrategy.isBatchingUpdates) { <del> var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(); transaction.perform( component.performUpdateIfNecessary, component, transaction ); ReactUpdates.ReactReconcileTransaction.release(transaction); callback && callback.call(component); </del> <ins> batchingStrategy.batchedUpdates(enqueueUpdate, component, callback); </ins> return; }

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> });