Add component stack to uncontrolled-to-controlled warning (#9416)
https://twitter.com/JulianVModesto/status/836089555304448000 https://twitter.com/abevoelker/status/836040051993763842
Ben Alpert committed
Apr 14, 2017 at 16:57 UTC
1447d9f1ae699b46c31d6c8b7f0e57212d6c7f1e
3 files changed
+90
-61
src/renderers/dom/fiber/wrappers/ReactDOMFiberInput.js
+10
-6
@@ -25,6 +25,10 @@ var ReactControlledValuePropTypes = require('ReactControlledValuePropTypes');
25
var ReactDOMComponentTree = require('ReactDOMComponentTree');
26
var {getCurrentFiberOwnerName} = require('ReactDebugCurrentFiber');
27
28
+if (__DEV__) {
29
+ var {getCurrentFiberStackAddendum} = require('ReactDebugCurrentFiber');
30
+}
31
+
32
var invariant = require('fbjs/lib/invariant');
33
var warning = require('fbjs/lib/warning');
34
@@ -154,12 +158,12 @@ var ReactDOMInput = {
158
) {
159
warning(
160
false,
157
- '%s is changing an uncontrolled input of type %s to be controlled. ' +
161
+ 'A component is changing an uncontrolled input of type %s to be controlled. ' +
162
'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
163
'Decide between using a controlled or uncontrolled input ' +
160
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
161
- getCurrentFiberOwnerName() || 'A component',
164
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components%s',
165
props.type,
166
+ getCurrentFiberStackAddendum(),
167
);
168
didWarnUncontrolledToControlled = true;
169
}
@@ -170,12 +174,12 @@ var ReactDOMInput = {
174
) {
175
warning(
176
false,
173
- '%s is changing a controlled input of type %s to be uncontrolled. ' +
177
+ 'A component is changing a controlled input of type %s to be uncontrolled. ' +
178
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
179
'Decide between using a controlled or uncontrolled input ' +
176
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
177
- getCurrentFiberOwnerName() || 'A component',
180
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components%s',
181
props.type,
182
+ getCurrentFiberStackAddendum(),
183
);
184
didWarnControlledToUncontrolled = true;
185
}
src/renderers/dom/shared/wrappers/__tests__/ReactDOMInput-test.js
+68
-48
@@ -21,6 +21,10 @@ describe('ReactDOMInput', () => {
21
var ReactTestUtils;
22
var inputValueTracking;
23
24
+ function normalizeCodeLocInfo(str) {
25
+ return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
26
+ }
27
+
28
function setUntrackedValue(elem, value) {
29
var tracker = inputValueTracking._getTrackerFromNode(elem);
30
var current = tracker.getValue();
@@ -738,11 +742,12 @@ describe('ReactDOMInput', () => {
742
ReactDOM.render(stub, container);
743
ReactDOM.render(<input type="text" />, container);
744
expectDev(console.error.calls.count()).toBe(1);
741
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
742
- 'A component is changing a controlled input of type text to be uncontrolled. ' +
745
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
746
+ 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
747
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
748
'Decide between using a controlled or uncontrolled input ' +
745
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
749
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
750
+ ' in input (at **)',
751
);
752
});
753
@@ -754,11 +759,12 @@ describe('ReactDOMInput', () => {
759
ReactDOM.render(stub, container);
760
ReactDOM.render(<input type="text" value={null} />, container);
761
expectDev(console.error.calls.count()).toBeGreaterThan(0);
757
- expectDev(console.error.calls.argsFor(1)[0]).toContain(
758
- 'A component is changing a controlled input of type text to be uncontrolled. ' +
762
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
763
+ 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
764
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
765
'Decide between using a controlled or uncontrolled input ' +
761
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
766
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
767
+ ' in input (at **)',
768
);
769
});
770
@@ -773,11 +779,12 @@ describe('ReactDOMInput', () => {
779
container,
780
);
781
expectDev(console.error.calls.count()).toBe(1);
776
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
777
- 'A component is changing a controlled input of type text to be uncontrolled. ' +
782
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
783
+ 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
784
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
785
'Decide between using a controlled or uncontrolled input ' +
780
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
786
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
787
+ ' in input (at **)',
788
);
789
});
790
@@ -787,11 +794,12 @@ describe('ReactDOMInput', () => {
794
ReactDOM.render(stub, container);
795
ReactDOM.render(<input type="text" value="controlled" />, container);
796
expectDev(console.error.calls.count()).toBe(1);
790
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
791
- 'A component is changing an uncontrolled input of type text to be controlled. ' +
797
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
798
+ 'Warning: A component is changing an uncontrolled input of type text to be controlled. ' +
799
'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
800
'Decide between using a controlled or uncontrolled input ' +
794
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
801
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
802
+ ' in input (at **)',
803
);
804
});
805
@@ -801,11 +809,12 @@ describe('ReactDOMInput', () => {
809
ReactDOM.render(stub, container);
810
ReactDOM.render(<input type="text" value="controlled" />, container);
811
expectDev(console.error.calls.count()).toBeGreaterThan(0);
804
- expectDev(console.error.calls.argsFor(1)[0]).toContain(
805
- 'A component is changing an uncontrolled input of type text to be controlled. ' +
812
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
813
+ 'Warning: A component is changing an uncontrolled input of type text to be controlled. ' +
814
'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
815
'Decide between using a controlled or uncontrolled input ' +
808
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
816
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
817
+ ' in input (at **)',
818
);
819
});
820
@@ -817,11 +826,12 @@ describe('ReactDOMInput', () => {
826
ReactDOM.render(stub, container);
827
ReactDOM.render(<input type="checkbox" />, container);
828
expectDev(console.error.calls.count()).toBe(1);
820
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
821
- 'A component is changing a controlled input of type checkbox to be uncontrolled. ' +
829
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
830
+ 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
831
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
832
'Decide between using a controlled or uncontrolled input ' +
824
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
833
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
834
+ ' in input (at **)',
835
);
836
});
837
@@ -833,11 +843,12 @@ describe('ReactDOMInput', () => {
843
ReactDOM.render(stub, container);
844
ReactDOM.render(<input type="checkbox" checked={null} />, container);
845
expectDev(console.error.calls.count()).toBe(1);
836
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
837
- 'A component is changing a controlled input of type checkbox to be uncontrolled. ' +
846
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
847
+ 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
848
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
849
'Decide between using a controlled or uncontrolled input ' +
840
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
850
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
851
+ ' in input (at **)',
852
);
853
});
854
@@ -849,11 +860,12 @@ describe('ReactDOMInput', () => {
860
ReactDOM.render(stub, container);
861
ReactDOM.render(<input type="checkbox" defaultChecked={true} />, container);
862
expectDev(console.error.calls.count()).toBe(1);
852
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
853
- 'A component is changing a controlled input of type checkbox to be uncontrolled. ' +
863
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
864
+ 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
865
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
866
'Decide between using a controlled or uncontrolled input ' +
856
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
867
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
868
+ ' in input (at **)',
869
);
870
});
871
@@ -863,11 +875,12 @@ describe('ReactDOMInput', () => {
875
ReactDOM.render(stub, container);
876
ReactDOM.render(<input type="checkbox" checked={true} />, container);
877
expectDev(console.error.calls.count()).toBe(1);
866
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
867
- 'A component is changing an uncontrolled input of type checkbox to be controlled. ' +
878
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
879
+ 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled. ' +
880
'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
881
'Decide between using a controlled or uncontrolled input ' +
870
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
882
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
883
+ ' in input (at **)',
884
);
885
});
886
@@ -877,11 +890,12 @@ describe('ReactDOMInput', () => {
890
ReactDOM.render(stub, container);
891
ReactDOM.render(<input type="checkbox" checked={true} />, container);
892
expectDev(console.error.calls.count()).toBe(1);
880
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
881
- 'A component is changing an uncontrolled input of type checkbox to be controlled. ' +
893
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
894
+ 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled. ' +
895
'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
896
'Decide between using a controlled or uncontrolled input ' +
884
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
897
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
898
+ ' in input (at **)',
899
);
900
});
901
@@ -891,11 +905,12 @@ describe('ReactDOMInput', () => {
905
ReactDOM.render(stub, container);
906
ReactDOM.render(<input type="radio" />, container);
907
expectDev(console.error.calls.count()).toBe(1);
894
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
895
- 'A component is changing a controlled input of type radio to be uncontrolled. ' +
908
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
909
+ 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
910
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
911
'Decide between using a controlled or uncontrolled input ' +
898
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
912
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
913
+ ' in input (at **)',
914
);
915
});
916
@@ -905,11 +920,12 @@ describe('ReactDOMInput', () => {
920
ReactDOM.render(stub, container);
921
ReactDOM.render(<input type="radio" checked={null} />, container);
922
expectDev(console.error.calls.count()).toBe(1);
908
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
909
- 'A component is changing a controlled input of type radio to be uncontrolled. ' +
923
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
924
+ 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
925
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
926
'Decide between using a controlled or uncontrolled input ' +
912
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
927
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
928
+ ' in input (at **)',
929
);
930
});
931
@@ -919,11 +935,12 @@ describe('ReactDOMInput', () => {
935
ReactDOM.render(stub, container);
936
ReactDOM.render(<input type="radio" defaultChecked={true} />, container);
937
expectDev(console.error.calls.count()).toBe(1);
922
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
923
- 'A component is changing a controlled input of type radio to be uncontrolled. ' +
938
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
939
+ 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
940
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
941
'Decide between using a controlled or uncontrolled input ' +
926
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
942
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
943
+ ' in input (at **)',
944
);
945
});
946
@@ -933,11 +950,12 @@ describe('ReactDOMInput', () => {
950
ReactDOM.render(stub, container);
951
ReactDOM.render(<input type="radio" checked={true} />, container);
952
expectDev(console.error.calls.count()).toBe(1);
936
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
937
- 'A component is changing an uncontrolled input of type radio to be controlled. ' +
953
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
954
+ 'Warning: A component is changing an uncontrolled input of type radio to be controlled. ' +
955
'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
956
'Decide between using a controlled or uncontrolled input ' +
940
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
957
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
958
+ ' in input (at **)',
959
);
960
});
961
@@ -947,11 +965,12 @@ describe('ReactDOMInput', () => {
965
ReactDOM.render(stub, container);
966
ReactDOM.render(<input type="radio" checked={true} />, container);
967
expectDev(console.error.calls.count()).toBe(1);
950
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
951
- 'A component is changing an uncontrolled input of type radio to be controlled. ' +
968
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
969
+ 'Warning: A component is changing an uncontrolled input of type radio to be controlled. ' +
970
'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
971
'Decide between using a controlled or uncontrolled input ' +
954
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
972
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
973
+ ' in input (at **)',
974
);
975
});
976
@@ -1002,11 +1021,12 @@ describe('ReactDOMInput', () => {
1021
container,
1022
);
1023
ReactDOM.render(<input type="radio" value="value" />, container);
1005
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
1006
- 'A component is changing a controlled input of type radio to be uncontrolled. ' +
1024
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1025
+ 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1026
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1027
'Decide between using a controlled or uncontrolled input ' +
1009
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
1028
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1029
+ ' in input (at **)',
1030
);
1031
});
1032
src/renderers/dom/stack/client/wrappers/ReactDOMInput.js
+12
-7
@@ -18,6 +18,12 @@ var ReactDOMComponentTree = require('ReactDOMComponentTree');
18
var invariant = require('fbjs/lib/invariant');
19
var warning = require('fbjs/lib/warning');
20
21
+if (__DEV__) {
22
+ var {
23
+ getStackAddendumByID,
24
+ } = require('ReactGlobalSharedState').ReactComponentTreeHook;
25
+}
26
+
27
var didWarnValueDefaultValue = false;
28
var didWarnCheckedDefaultChecked = false;
29
var didWarnControlledToUncontrolled = false;
@@ -137,7 +143,6 @@ var ReactDOMInput = {
143
144
if (__DEV__) {
145
var controlled = isControlled(props);
140
- var owner = inst._currentElement._owner;
146
147
if (
148
!inst._wrapperState.controlled &&
@@ -146,12 +151,12 @@ var ReactDOMInput = {
151
) {
152
warning(
153
false,
149
- '%s is changing an uncontrolled input of type %s to be controlled. ' +
154
+ 'A component is changing an uncontrolled input of type %s to be controlled. ' +
155
'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
156
'Decide between using a controlled or uncontrolled input ' +
152
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
153
- (owner && owner.getName()) || 'A component',
157
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components%s',
158
props.type,
159
+ getStackAddendumByID(inst._debugID),
160
);
161
didWarnUncontrolledToControlled = true;
162
}
@@ -162,12 +167,12 @@ var ReactDOMInput = {
167
) {
168
warning(
169
false,
165
- '%s is changing a controlled input of type %s to be uncontrolled. ' +
170
+ 'A component is changing a controlled input of type %s to be uncontrolled. ' +
171
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
172
'Decide between using a controlled or uncontrolled input ' +
168
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
169
- (owner && owner.getName()) || 'A component',
173
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components%s',
174
props.type,
175
+ getStackAddendumByID(inst._debugID),
176
);
177
didWarnControlledToUncontrolled = true;
178
}