diff --git a/src/renderers/dom/client/wrappers/ReactDOMInput.js b/src/renderers/dom/client/wrappers/ReactDOMInput.js
index 2ce3826c6a17e..c4bcca0c8e31c 100644
--- a/src/renderers/dom/client/wrappers/ReactDOMInput.js
+++ b/src/renderers/dom/client/wrappers/ReactDOMInput.js
@@ -48,6 +48,11 @@ function warnIfValueIsNull(props) {
}
}
+function isControlled(props) {
+ var usesChecked = props.type === 'checkbox' || props.type === 'radio';
+ return usesChecked ? props.checked !== undefined : props.value !== undefined;
+}
+
/**
* Implements an host component that allows setting these optional
* props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
@@ -156,7 +161,7 @@ var ReactDOMInput = {
};
if (__DEV__) {
- inst._wrapperState.controlled = props.checked !== undefined || props.value !== undefined;
+ inst._wrapperState.controlled = isControlled(props);
}
},
@@ -166,14 +171,10 @@ var ReactDOMInput = {
if (__DEV__) {
warnIfValueIsNull(props);
- var defaultValue = props.defaultChecked || props.defaultValue;
- var controlled = props.checked !== undefined || props.value !== undefined;
+ var controlled = isControlled(props);
var owner = inst._currentElement._owner;
- if (
- !inst._wrapperState.controlled &&
- controlled && !didWarnUncontrolledToControlled
- ) {
+ if (!inst._wrapperState.controlled && controlled && !didWarnUncontrolledToControlled) {
warning(
false,
'%s is changing an uncontrolled input of type %s to be controlled. ' +
@@ -185,11 +186,7 @@ var ReactDOMInput = {
);
didWarnUncontrolledToControlled = true;
}
- if (
- inst._wrapperState.controlled &&
- (defaultValue || !controlled) &&
- !didWarnControlledToUncontrolled
- ) {
+ if (inst._wrapperState.controlled && !controlled && !didWarnControlledToUncontrolled) {
warning(
false,
'%s is changing a controlled input of type %s to be uncontrolled. ' +
diff --git a/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js b/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
index 090207f301c40..46fe29f49055a 100644
--- a/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
+++ b/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
@@ -662,6 +662,43 @@ describe('ReactDOMInput', function() {
);
});
+ it('should not warn if radio value changes but never becomes controlled', function() {
+ var container = document.createElement('div');
+ ReactDOM.render(, container);
+ ReactDOM.render(, container);
+ ReactDOM.render(, container);
+ ReactDOM.render( null} />, container);
+ ReactDOM.render(, container);
+ expect(console.error.calls.count()).toBe(0);
+ });
+
+ it('should not warn if radio value changes but never becomes uncontrolled', function() {
+ var container = document.createElement('div');
+ ReactDOM.render( null} />, container);
+ ReactDOM.render(
+ null}
+ />, container);
+ console.log(console.error.calls.argsFor(0)[0]);
+ expect(console.error.calls.count()).toBe(0);
+ });
+
+ it('should warn if radio checked false changes to become uncontrolled', function() {
+ var container = document.createElement('div');
+ ReactDOM.render( null} />, container);
+ ReactDOM.render(, container);
+ expect(console.error.calls.argsFor(0)[0]).toContain(
+ 'A component is changing a controlled input of type radio to be uncontrolled. ' +
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
+ 'Decide between using a controlled or uncontrolled input ' +
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components'
+ );
+ });
+
it('sets type before value always', function() {
if (!ReactDOMFeatureFlags.useCreateElement) {
return;