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 2b78df3bc7c26..f0333cfa416d0 100644 --- a/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js +++ b/src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js @@ -653,6 +653,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;