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,