@samitouri / QOS-React-2 / commits / 1447d9f1ae

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 }