Skip to content

Commit 195e307

Browse files
committed
Avoid validation warning when inputs change type
For controlled inputs, `updateWrapper` was getting called before the `type` prop had a chance to update. This could lead to a case where switching from the `text` to `number` type caused a validation error that would prevent the proper input value from being assigned. This commit moves the call to `ReactDOMInput.updateWrapper` below `_updateProperties` to avoid this situation.
1 parent 3fd5826 commit 195e307

File tree

2 files changed

+36
-6
lines changed

2 files changed

+36
-6
lines changed

src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js

+21
Original file line numberDiff line numberDiff line change
@@ -769,4 +769,25 @@ describe('ReactDOMInput', function() {
769769
);
770770
expect(input.value).toBe('hi');
771771
});
772+
773+
it('does not raise a validation warning when it switches types', function() {
774+
var Input = React.createClass({
775+
getInitialState() {
776+
return { type: 'number', value: 1000 };
777+
},
778+
render() {
779+
var { value, type } = this.state;
780+
return (<input onChange={() => {}} type={type} value={value} />);
781+
},
782+
});
783+
784+
var input = ReactTestUtils.renderIntoDocument(<Input />);
785+
var node = ReactDOM.findDOMNode(input);
786+
787+
// If the value is set before the type, a validation warning will raise and
788+
// the value will not be assigned.
789+
input.setState({ type: 'text', value: 'Test' });
790+
expect(node.value).toEqual('Test');
791+
});
792+
772793
});

src/renderers/dom/shared/ReactDOMComponent.js

+15-6
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,6 @@ ReactDOMComponent.Mixin = {
907907
nextProps = ReactDOMButton.getHostProps(this, nextProps);
908908
break;
909909
case 'input':
910-
ReactDOMInput.updateWrapper(this);
911910
lastProps = ReactDOMInput.getHostProps(this, lastProps);
912911
nextProps = ReactDOMInput.getHostProps(this, nextProps);
913912
break;
@@ -920,7 +919,6 @@ ReactDOMComponent.Mixin = {
920919
nextProps = ReactDOMSelect.getHostProps(this, nextProps);
921920
break;
922921
case 'textarea':
923-
ReactDOMTextarea.updateWrapper(this);
924922
lastProps = ReactDOMTextarea.getHostProps(this, lastProps);
925923
nextProps = ReactDOMTextarea.getHostProps(this, nextProps);
926924
break;
@@ -935,10 +933,21 @@ ReactDOMComponent.Mixin = {
935933
context
936934
);
937935

938-
if (this._tag === 'select') {
939-
// <select> value update needs to occur after <option> children
940-
// reconciliation
941-
transaction.getReactMountReady().enqueue(postUpdateSelectWrapper, this);
936+
switch (this._tag) {
937+
case 'textarea':
938+
ReactDOMTextarea.updateWrapper(this);
939+
break;
940+
case 'input':
941+
// Update the wrapper around inputs *after* updating props. This has to
942+
// happen after `_updateDOMProperties`. Otherwise HTML5 input validations
943+
// raise warnings and prevent the new value from being assigned.
944+
ReactDOMInput.updateWrapper(this);
945+
break;
946+
case 'select':
947+
// <select> value update needs to occur after <option> children
948+
// reconciliation
949+
transaction.getReactMountReady().enqueue(postUpdateSelectWrapper, this);
950+
break;
942951
}
943952

944953
if (__DEV__) {

0 commit comments

Comments
 (0)