Fix controlled/uncontrolled validation for radio+checkbox inputs (#7003)
Jim committed
Jun 9, 2016 at 07:58 UTC
0bb0fe8d0006c6f26e64723ffdbce3baf74c9ab1
2 files changed
+46
-12
src/renderers/dom/client/wrappers/ReactDOMInput.js
+9
-12
@@ -48,6 +48,11 @@ function warnIfValueIsNull(props) {
48
}
49
}
50
51
+function isControlled(props) {
52
+ var usesChecked = props.type === 'checkbox' || props.type === 'radio';
53
+ return usesChecked ? props.checked !== undefined : props.value !== undefined;
54
+}
55
+
56
/**
57
* Implements an <input> host component that allows setting these optional
58
* props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
@@ -156,7 +161,7 @@ var ReactDOMInput = {
161
};
162
163
if (__DEV__) {
159
- inst._wrapperState.controlled = props.checked !== undefined || props.value !== undefined;
164
+ inst._wrapperState.controlled = isControlled(props);
165
}
166
},
167
@@ -166,14 +171,10 @@ var ReactDOMInput = {
171
if (__DEV__) {
172
warnIfValueIsNull(props);
173
169
- var defaultValue = props.defaultChecked || props.defaultValue;
170
- var controlled = props.checked !== undefined || props.value !== undefined;
174
+ var controlled = isControlled(props);
175
var owner = inst._currentElement._owner;
176
173
- if (
174
- !inst._wrapperState.controlled &&
175
- controlled && !didWarnUncontrolledToControlled
176
- ) {
177
+ if (!inst._wrapperState.controlled && controlled && !didWarnUncontrolledToControlled) {
178
warning(
179
false,
180
'%s is changing an uncontrolled input of type %s to be controlled. ' +
@@ -185,11 +186,7 @@ var ReactDOMInput = {
186
);
187
didWarnUncontrolledToControlled = true;
188
}
188
- if (
189
- inst._wrapperState.controlled &&
190
- (defaultValue || !controlled) &&
191
- !didWarnControlledToUncontrolled
192
- ) {
189
+ if (inst._wrapperState.controlled && !controlled && !didWarnControlledToUncontrolled) {
190
warning(
191
false,
192
'%s is changing a controlled input of type %s to be uncontrolled. ' +
src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
+37
@@ -662,6 +662,43 @@ describe('ReactDOMInput', function() {
662
);
663
});
664
665
+ it('should not warn if radio value changes but never becomes controlled', function() {
666
+ var container = document.createElement('div');
667
+ ReactDOM.render(<input type="radio" value="value" />, container);
668
+ ReactDOM.render(<input type="radio" />, container);
669
+ ReactDOM.render(<input type="radio" value="value" defaultChecked={true} />, container);
670
+ ReactDOM.render(<input type="radio" value="value" onChange={() => null} />, container);
671
+ ReactDOM.render(<input type="radio" />, container);
672
+ expect(console.error.calls.count()).toBe(0);
673
+ });
674
+
675
+ it('should not warn if radio value changes but never becomes uncontrolled', function() {
676
+ var container = document.createElement('div');
677
+ ReactDOM.render(<input type="radio" checked={false} onChange={() => null} />, container);
678
+ ReactDOM.render(
679
+ <input
680
+ type="radio"
681
+ value="value"
682
+ defaultChecked={true}
683
+ checked={false}
684
+ onChange={() => null}
685
+ />, container);
686
+ console.log(console.error.calls.argsFor(0)[0]);
687
+ expect(console.error.calls.count()).toBe(0);
688
+ });
689
+
690
+ it('should warn if radio checked false changes to become uncontrolled', function() {
691
+ var container = document.createElement('div');
692
+ ReactDOM.render(<input type="radio" value="value" checked={false} onChange={() => null} />, container);
693
+ ReactDOM.render(<input type="radio" value="value" />, container);
694
+ expect(console.error.calls.argsFor(0)[0]).toContain(
695
+ 'A component is changing a controlled input of type radio to be uncontrolled. ' +
696
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
697
+ 'Decide between using a controlled or uncontrolled input ' +
698
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components'
699
+ );
700
+ });
701
+
702
it('sets type before value always', function() {
703
if (!ReactDOMFeatureFlags.useCreateElement) {
704
return;