diff --git a/src/renderers/dom/client/wrappers/ReactDOMInput.js b/src/renderers/dom/client/wrappers/ReactDOMInput.js
index 881e8c146b44c..e52c199b24a15 100644
--- a/src/renderers/dom/client/wrappers/ReactDOMInput.js
+++ b/src/renderers/dom/client/wrappers/ReactDOMInput.js
@@ -25,6 +25,8 @@ var didWarnCheckedLink = false;
var didWarnValueNull = false;
var didWarnValueDefaultValue = false;
var didWarnCheckedDefaultChecked = false;
+var didWarnControlledToUncontrolled = false;
+var didWarnUncontrolledToControlled = false;
function forceUpdateIfMounted() {
if (this._rootNodeID) {
@@ -144,6 +146,10 @@ var ReactDOMInput = {
listeners: null,
onChange: _handleChange.bind(inst),
};
+
+ if (__DEV__) {
+ inst._wrapperState.controlled = props.checked !== undefined || props.value !== undefined;
+ }
},
updateWrapper: function(inst) {
@@ -151,6 +157,43 @@ var ReactDOMInput = {
if (__DEV__) {
warnIfValueIsNull(props);
+
+ var initialValue = inst._wrapperState.initialChecked || inst._wrapperState.initialValue;
+ var defaultValue = props.defaultChecked || props.defaultValue;
+ var controlled = props.checked !== undefined || props.value !== undefined;
+ var owner = inst._currentElement._owner;
+
+ if (
+ (initialValue || !inst._wrapperState.controlled) &&
+ controlled && !didWarnUncontrolledToControlled
+ ) {
+ warning(
+ false,
+ '%s is changing a uncontrolled input of type %s to be controlled. ' +
+ 'Input elements should not switch from uncontrolled to controlled (or viceversa). ' +
+ 'Decide between using a controlled or uncontrolled input ' +
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
+ owner && owner.getName() || 'A component',
+ props.type
+ );
+ didWarnUncontrolledToControlled = true;
+ }
+ if (
+ inst._wrapperState.controlled &&
+ (defaultValue || !controlled) &&
+ !didWarnControlledToUncontrolled
+ ) {
+ warning(
+ false,
+ '%s is changing a controlled input of type %s to be uncontrolled. ' +
+ 'Input elements should not switch from controlled to uncontrolled (or viceversa). ' +
+ 'Decide between using a controlled or uncontrolled input ' +
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
+ owner && owner.getName() || 'A component',
+ props.type
+ );
+ didWarnControlledToUncontrolled = true;
+ }
}
// TODO: Shouldn't this be getChecked(props)?
diff --git a/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js b/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
index 196ff290fafe0..406b25fd00834 100644
--- a/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
+++ b/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
@@ -451,6 +451,132 @@ describe('ReactDOMInput', function() {
expect(console.error.argsForCall.length).toBe(1);
});
+ it('should warn if controlled input switches to uncontrolled', function() {
+ var stub = ;
+ var container = document.createElement('div');
+ ReactDOM.render(stub, container);
+ ReactDOM.render(, container);
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toContain(
+ 'A component is changing a controlled input of type text to be uncontrolled. ' +
+ 'Input elements should not switch from controlled to uncontrolled (or viceversa). ' +
+ 'Decide between using a controlled or uncontrolled input ' +
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components'
+ );
+ });
+
+ it('should warn if controlled input switches to uncontrolled with defaultValue', function() {
+ var stub = ;
+ var container = document.createElement('div');
+ ReactDOM.render(stub, container);
+ ReactDOM.render(, container);
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toContain(
+ 'A component is changing a controlled input of type text to be uncontrolled. ' +
+ 'Input elements should not switch from controlled to uncontrolled (or viceversa). ' +
+ 'Decide between using a controlled or uncontrolled input ' +
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components'
+ );
+ });
+
+ it('should warn if uncontrolled input switches to controlled', function() {
+ var stub = ;
+ var container = document.createElement('div');
+ ReactDOM.render(stub, container);
+ ReactDOM.render(, container);
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toContain(
+ 'A component is changing a uncontrolled input of type text to be controlled. ' +
+ 'Input elements should not switch from uncontrolled to controlled (or viceversa). ' +
+ 'Decide between using a controlled or uncontrolled input ' +
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components'
+ );
+ });
+
+ it('should warn if controlled checkbox switches to uncontrolled', function() {
+ var stub = ;
+ var container = document.createElement('div');
+ ReactDOM.render(stub, container);
+ ReactDOM.render(, container);
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toContain(
+ 'A component is changing a controlled input of type checkbox to be uncontrolled. ' +
+ 'Input elements should not switch from controlled to uncontrolled (or viceversa). ' +
+ 'Decide between using a controlled or uncontrolled input ' +
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components'
+ );
+ });
+
+ it('should warn if controlled checkbox switches to uncontrolled with defaultChecked', function() {
+ var stub = ;
+ var container = document.createElement('div');
+ ReactDOM.render(stub, container);
+ ReactDOM.render(, container);
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toContain(
+ 'A component is changing a controlled input of type checkbox to be uncontrolled. ' +
+ 'Input elements should not switch from controlled to uncontrolled (or viceversa). ' +
+ 'Decide between using a controlled or uncontrolled input ' +
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components'
+ );
+ });
+
+ it('should warn if uncontrolled checkbox switches to controlled', function() {
+ var stub = ;
+ var container = document.createElement('div');
+ ReactDOM.render(stub, container);
+ ReactDOM.render(, container);
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toContain(
+ 'A component is changing a uncontrolled input of type checkbox to be controlled. ' +
+ 'Input elements should not switch from uncontrolled to controlled (or viceversa). ' +
+ 'Decide between using a controlled or uncontrolled input ' +
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components'
+ );
+ });
+
+ it('should warn if controlled radio switches to uncontrolled', function() {
+ var stub = ;
+ var container = document.createElement('div');
+ ReactDOM.render(stub, container);
+ ReactDOM.render(, container);
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[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 viceversa). ' +
+ 'Decide between using a controlled or uncontrolled input ' +
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components'
+ );
+ });
+
+ it('should warn if controlled radio switches to uncontrolled with defaultChecked', function() {
+ var stub = ;
+ var container = document.createElement('div');
+ ReactDOM.render(stub, container);
+ ReactDOM.render(, container);
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[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 viceversa). ' +
+ 'Decide between using a controlled or uncontrolled input ' +
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components'
+ );
+ });
+
+ it('should warn if uncontrolled radio switches to controlled', function() {
+ var stub = ;
+ var container = document.createElement('div');
+ ReactDOM.render(stub, container);
+ ReactDOM.render(, container);
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toContain(
+ 'A component is changing a uncontrolled input of type radio to be controlled. ' +
+ 'Input elements should not switch from uncontrolled to controlled (or viceversa). ' +
+ '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() {
var log = [];
var originalCreateElement = document.createElement;