From 8f0bf05da5520959cd9ea66c81591a0f3e9ab532 Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Thu, 25 Sep 2014 14:19:08 +0200 Subject: [PATCH] Remount ReactDOMInput when changing type --- .../ui/dom/components/ReactDOMInput.js | 33 +++++++++++--- .../__tests__/ReactDOMInput-test.js | 44 +++++++++++++++++-- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/browser/ui/dom/components/ReactDOMInput.js b/src/browser/ui/dom/components/ReactDOMInput.js index 49f783c1e12..799f368e975 100644 --- a/src/browser/ui/dom/components/ReactDOMInput.js +++ b/src/browser/ui/dom/components/ReactDOMInput.js @@ -51,6 +51,22 @@ 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', @@ -58,17 +74,18 @@ var ReactDOMInput = ReactClass.createClass({ 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; @@ -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; diff --git a/src/browser/ui/dom/components/__tests__/ReactDOMInput-test.js b/src/browser/ui/dom/components/__tests__/ReactDOMInput-test.js index f67d43d1b8e..0c19e13f25a 100644 --- a/src/browser/ui/dom/components/__tests__/ReactDOMInput-test.js +++ b/src/browser/ui/dom/components/__tests__/ReactDOMInput-test.js @@ -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'); }); @@ -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'); }); @@ -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'); }); @@ -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( + , + node + ); + + + }); + + it('should remount when changing type', function() { + var node = document.createElement('div'); + + React.render( + , + node + ); + + var stub = React.render( + , + node + ); + + expect(stub.getDOMNode().value).toBe('foo'); + + stub = React.render( + , + node + ); + + expect(stub.getDOMNode().value).toBe('bar'); + }); + it('should support ReactLink', function() { var link = new ReactLink('yolo', mocks.getMockFunction()); var instance = ;