Skip to content

Commit

Permalink
Fix controlled/uncontrolled validation for radio+checkbox inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
jimfb authored and jim committed Jun 9, 2016
1 parent c47830d commit 31c0b79
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 11 deletions.
20 changes: 9 additions & 11 deletions src/renderers/dom/client/wrappers/ReactDOMInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ function warnIfValueIsNull(props) {
}
}

function isControlled(props) {
return ((props.type === 'checkbox' || props.type === 'radio') && props.checked !== undefined) ||
(props.type !== 'checkbox' && props.type !== 'radio' && props.value !== undefined);
}

/**
* Implements an <input> host component that allows setting these optional
* props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
Expand Down Expand Up @@ -156,7 +161,7 @@ var ReactDOMInput = {
};

if (__DEV__) {
inst._wrapperState.controlled = props.checked !== undefined || props.value !== undefined;
inst._wrapperState.controlled = isControlled(props);
}
},

Expand All @@ -167,13 +172,10 @@ var ReactDOMInput = {
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. ' +
Expand All @@ -185,11 +187,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. ' +
Expand Down
37 changes: 37 additions & 0 deletions src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(<input type="radio" value="value" />, container);
ReactDOM.render(<input type="radio" />, container);
ReactDOM.render(<input type="radio" value="value" defaultChecked={true} />, container);
ReactDOM.render(<input type="radio" value="value" onChange={() => null} />, container);
ReactDOM.render(<input type="radio" />, 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(<input type="radio" checked={false} onChange={() => null} />, container);
ReactDOM.render(
<input
type="radio"
value="value"
defaultChecked={true}
checked={false}
onChange={() => 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(<input type="radio" value="value" checked={false} onChange={() => null} />, container);
ReactDOM.render(<input type="radio" value="value" />, 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;
Expand Down

0 comments on commit 31c0b79

Please sign in to comment.