diff --git a/src/browser/ui/dom/components/ReactDOMInput.js b/src/browser/ui/dom/components/ReactDOMInput.js index 0437f60513c..1fd43512516 100644 --- a/src/browser/ui/dom/components/ReactDOMInput.js +++ b/src/browser/ui/dom/components/ReactDOMInput.js @@ -25,6 +25,7 @@ var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin'); var ReactCompositeComponent = require('ReactCompositeComponent'); var ReactDOM = require('ReactDOM'); var ReactMount = require('ReactMount'); +var ReactUpdates = require('ReactUpdates'); var invariant = require('invariant'); var merge = require('merge'); @@ -34,6 +35,13 @@ var input = ReactDOM.input; var instancesByReactID = {}; +function forceUpdateIfMounted() { + /*jshint validthis:true */ + if (this.isMounted()) { + this.forceUpdate(); + } +} + /** * Implements an native component that allows setting these optional * props: `checked`, `value`, `defaultChecked`, and `defaultValue`. @@ -58,16 +66,11 @@ var ReactDOMInput = ReactCompositeComponent.createClass({ getInitialState: function() { var defaultValue = this.props.defaultValue; return { - checked: this.props.defaultChecked || false, - value: defaultValue != null ? defaultValue : null + initialChecked: this.props.defaultChecked || false, + initialValue: defaultValue != null ? defaultValue : null }; }, - shouldComponentUpdate: function() { - // Defer any updates to this component during the `onChange` handler. - return !this._isChanging; - }, - render: function() { // Clone `this.props` so we don't mutate the input. var props = merge(this.props); @@ -76,10 +79,10 @@ var ReactDOMInput = ReactCompositeComponent.createClass({ props.defaultValue = null; var value = LinkedValueUtils.getValue(this); - props.value = value != null ? value : this.state.value; + props.value = value != null ? value : this.state.initialValue; var checked = LinkedValueUtils.getChecked(this); - props.checked = checked != null ? checked : this.state.checked; + props.checked = checked != null ? checked : this.state.initialChecked; props.onChange = this._handleChange; @@ -119,14 +122,12 @@ var ReactDOMInput = ReactCompositeComponent.createClass({ var returnValue; var onChange = LinkedValueUtils.getOnChange(this); if (onChange) { - this._isChanging = true; returnValue = onChange.call(this, event); - this._isChanging = false; } - this.setState({ - checked: event.target.checked, - value: event.target.value - }); + // Here we use setImmediate to wait until all updates have propagated, which + // is important when using controlled components within layers: + // https://github.com/facebook/react/issues/1698 + ReactUpdates.setImmediate(forceUpdateIfMounted, this); var name = this.props.name; if (this.props.type === 'radio' && name != null) { @@ -164,13 +165,10 @@ var ReactDOMInput = ReactCompositeComponent.createClass({ 'ReactDOMInput: Unknown radio button ID %s.', otherID ); - // In some cases, this will actually change the `checked` state value. - // In other cases, there's no change but this forces a reconcile upon - // which componentDidUpdate will reset the DOM property to whatever it - // should be. - otherInstance.setState({ - checked: false - }); + // If this is a controlled radio button group, forcing the input that + // was previously checked to update will cause it to be come re-checked + // as appropriate. + ReactUpdates.setImmediate(forceUpdateIfMounted, otherInstance); } } diff --git a/src/browser/ui/dom/components/ReactDOMSelect.js b/src/browser/ui/dom/components/ReactDOMSelect.js index 9a3a3e08182..3b965c60bad 100644 --- a/src/browser/ui/dom/components/ReactDOMSelect.js +++ b/src/browser/ui/dom/components/ReactDOMSelect.js @@ -23,12 +23,21 @@ var LinkedValueUtils = require('LinkedValueUtils'); var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin'); var ReactCompositeComponent = require('ReactCompositeComponent'); var ReactDOM = require('ReactDOM'); +var ReactUpdates = require('ReactUpdates'); var merge = require('merge'); // Store a reference to the