diff --git a/src/eventPlugins/ChangeEventPlugin.js b/src/eventPlugins/ChangeEventPlugin.js index ad8fa70f120..61a9324d17f 100644 --- a/src/eventPlugins/ChangeEventPlugin.js +++ b/src/eventPlugins/ChangeEventPlugin.js @@ -22,6 +22,7 @@ var EventConstants = require('EventConstants'); var EventPluginHub = require('EventPluginHub'); var EventPropagators = require('EventPropagators'); var ExecutionEnvironment = require('ExecutionEnvironment'); +var ReactUpdates = require('ReactUpdates'); var SyntheticEvent = require('SyntheticEvent'); var isEventSupported = require('isEventSupported'); @@ -73,10 +74,21 @@ function manualDispatchChangeEvent(nativeEvent) { ); EventPropagators.accumulateTwoPhaseDispatches(event); - // If change bubbled, we'd just bind to it like all the other events - // and have it go through ReactEventTopLevelCallback. Since it doesn't, we - // manually listen for the change event and so we have to enqueue and + // If change and propertychange bubbled, we'd just bind to it like all the + // other events and have it go through ReactEventTopLevelCallback. Since it + // doesn't, we manually listen for the events and so we have to enqueue and // process the abstract event manually. + // + // Batching is necessary here in order to ensure that all event handlers run + // before the next rerender (including event handlers attached to ancestor + // elements instead of directly on the input). Without this, controlled + // components don't work properly in conjunction with event bubbling because + // the component is rerendered and the value reverted before all the event + // handlers can run. See https://github.com/facebook/react/issues/708. + ReactUpdates.batchedUpdates(runEventInBatch, event); +} + +function runEventInBatch(event) { EventPluginHub.enqueueEvents(event); EventPluginHub.processEventQueue(); }