diff --git a/src/renderers/dom/client/wrappers/ReactDOMInput.js b/src/renderers/dom/client/wrappers/ReactDOMInput.js index 682a34961a63c..913eb8a17b4c3 100644 --- a/src/renderers/dom/client/wrappers/ReactDOMInput.js +++ b/src/renderers/dom/client/wrappers/ReactDOMInput.js @@ -69,12 +69,18 @@ var ReactDOMInput = { var nativeProps = assign({}, props, { defaultChecked: undefined, - defaultValue: undefined, - value: value != null ? value : inst._wrapperState.initialValue, checked: checked != null ? checked : inst._wrapperState.initialChecked, onChange: inst._wrapperState.onChange, }); + if (value !== undefined) { + // for controlled inputs, use defaultValue as an initial value + nativeProps.value = value != null ? value : props.defaultValue; + } else { + // for uncontrolled inputs, pass defaultValue property to DOM element + nativeProps.defaultValue = props.defaultValue; + } + return nativeProps; }, @@ -103,10 +109,8 @@ var ReactDOMInput = { warnIfValueIsNull(props); } - var defaultValue = props.defaultValue; inst._wrapperState = { initialChecked: props.defaultChecked || false, - initialValue: defaultValue != null ? defaultValue : null, listeners: null, onChange: _handleChange.bind(inst), }; diff --git a/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js b/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js index ffa90a059d4cc..7f734b68da288 100644 --- a/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js +++ b/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js @@ -36,6 +36,7 @@ describe('ReactDOMInput', function() { stub = ReactTestUtils.renderIntoDocument(stub); var node = ReactDOM.findDOMNode(stub); + expect(node.getAttribute('value')).toBe('0'); expect(node.value).toBe('0'); }); @@ -55,6 +56,68 @@ describe('ReactDOMInput', function() { expect(node.value).toBe('false'); }); + it('should update `defaultValue` for uncontrolled input', function() { + var container = document.createElement('div'); + + var el = ReactDOM.render(, container); + var node = ReactDOM.findDOMNode(el); + + expect(node.value).toBe('0'); + + ReactDOM.render(, container); + + expect(node.value).toBe('1'); + }); + + it('should take `defaultValue` for a controlled input', function() { + var container = document.createElement('div'); + + var el = ReactDOM.render(, container); + var node = ReactDOM.findDOMNode(el); + + expect(node.value).toBe('0'); + + ReactDOM.render(, container); + + expect(node.value).toBe('1'); + + ReactDOM.render(, container); + + expect(node.value).toBe('3'); + + ReactDOM.render(, container); + + expect(node.value).toBe('5'); + expect(node.getAttribute('value')).toBe('4'); + expect(node.defaultValue).toBe('4'); + }); + + it('should update `value` when changing to controlled input', function() { + var container = document.createElement('div'); + + var el = ReactDOM.render(, container); + var node = ReactDOM.findDOMNode(el); + + expect(node.value).toBe('0'); + + ReactDOM.render(, container); + + expect(node.value).toBe('1'); + }); + + it('should clear `value` when changing to uncontrolled input', function() { + var container = document.createElement('div'); + + var el = ReactDOM.render(, container); + var node = ReactDOM.findDOMNode(el); + + expect(node.value).toBe('0'); + + ReactDOM.render(, container); + + expect(node.value).toBe(''); + }); + it('should display "foobar" for `defaultValue` of `objToString`', function() { var objToString = { toString: function() { diff --git a/src/renderers/dom/shared/HTMLDOMPropertyConfig.js b/src/renderers/dom/shared/HTMLDOMPropertyConfig.js index bcc9fd05c3680..91990dbaaa09e 100644 --- a/src/renderers/dom/shared/HTMLDOMPropertyConfig.js +++ b/src/renderers/dom/shared/HTMLDOMPropertyConfig.js @@ -81,6 +81,7 @@ var HTMLDOMPropertyConfig = { data: null, // For `` acts as `src`. dateTime: MUST_USE_ATTRIBUTE, default: HAS_BOOLEAN_VALUE, + defaultValue: MUST_USE_PROPERTY, defer: HAS_BOOLEAN_VALUE, dir: MUST_USE_ATTRIBUTE, disabled: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE,