Skip to content

Remount ReactDOMInput when changing type #2242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions src/browser/ui/dom/components/ReactDOMInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,41 @@ function forceUpdateIfMounted() {
*
* @see http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
*/

function getKeyForType(props) {
// Returning `'text'` makes sense but will throw in IE8 when mutated as an
// attribute and would cause a toggling attribute in other browsers.
// Ultimately very much an edge case, but `''` seems overall preferable.
return props.type != null ? props.type : '';
}

function getInitialState(props) {
var defaultValue = props.defaultValue;
return {
initialChecked: props.defaultChecked || false,
initialValue: defaultValue != null ? defaultValue : null
};
}

var ReactDOMInput = ReactClass.createClass({
displayName: 'ReactDOMInput',
tagName: 'INPUT',

mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin],

getInitialState: function() {
var defaultValue = this.props.defaultValue;
return {
initialChecked: this.props.defaultChecked || false,
initialValue: defaultValue != null ? defaultValue : null
};
return getInitialState(this.props);
},

render: function() {
// Clone `this.props` so we don't mutate the input.
var props = assign({}, this.props);

// IE8 does not support changing input `type`. IE does not remember
// `checked` if `type` isn't `checkbox` or `radio`. It's also really weird
// to reconcile inputs with different `type` as if they were the same.
props.key = getKeyForType(props);

props.defaultChecked = null;
props.defaultValue = null;

Expand All @@ -83,6 +100,12 @@ var ReactDOMInput = ReactClass.createClass({
return input(props, this.props.children);
},

componentWillReceiveProps: function(nextProps) {
if (getKeyForType(this.props) !== getKeyForType(nextProps)) {
this.replaceState(getInitialState(nextProps));
}
},

componentDidMount: function() {
var id = ReactMount.getID(findDOMNode(this));
instancesByReactID[id] = this;
Expand Down
44 changes: 41 additions & 3 deletions src/browser/ui/dom/components/__tests__/ReactDOMInput-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ describe('ReactDOMInput', function() {

expect(node.value).toBe('yolo');

stub.replaceProps({value: true, onChange: emptyFunction});
stub.replaceProps({value: true, type: 'text', onChange: emptyFunction});
expect(node.value).toEqual('true');
});

Expand All @@ -93,7 +93,7 @@ describe('ReactDOMInput', function() {

expect(node.value).toBe('yolo');

stub.replaceProps({value: false});
stub.replaceProps({value: false, type: 'text'});
expect(node.value).toEqual('false');
});

Expand All @@ -110,7 +110,11 @@ describe('ReactDOMInput', function() {
}
};

stub.replaceProps({value: objToString, onChange: emptyFunction});
stub.replaceProps({
value: objToString,
type: 'text',
onChange: emptyFunction
});
expect(node.value).toEqual('foobar');
});

Expand Down Expand Up @@ -196,6 +200,40 @@ describe('ReactDOMInput', function() {
expect(cNode.checked).toBe(true);
});

it('should not remount when adding type text', function() {
var node = document.createElement('div');

React.render(
<input defaultValue="foo" />,
node
);


});

it('should remount when changing type', function() {
var node = document.createElement('div');

React.render(
<input defaultValue="foobar" />,
node
);

var stub = React.render(
<input type="text" defaultValue="foo" />,
node
);

expect(stub.getDOMNode().value).toBe('foo');

stub = React.render(
<input type="foobar" defaultValue="bar" />,
node
);

expect(stub.getDOMNode().value).toBe('bar');
});

it('should support ReactLink', function() {
var link = new ReactLink('yolo', mocks.getMockFunction());
var instance = <input type="text" valueLink={link} />;
Expand Down