@samitouri / QOS-React-2 / commits / 544d5c7208

Fixed invalid prop types error message to be more specific (#11308)

* Modified tests and corrected error message. #3 * Fixed syntax issues. #3 * Modified test. #3 * Prettified. #3 * Changed warning message to handle true and false boolean values. #3 * Changed test to contain undefined instead of value. #3 * Simplified branch structure. #3 * Refactored branching logic. #3 * Refactored falsy warning message and tests. #3 * Changed condition to attribute name. #3 * Refactored falsy and truthy warning messages with tests updated. #3 * Added missing character. #3 * Fixed warning message. #3 * Cleared extra whitespace. #3 * Refactored warning messages to be clear. #3 * Prettified. #3 * Grammar fix * Tweak unrelated warning The message didn't make sense because it appears for *any* attributes, not just numeric ones. * Tweak the message for more clarity * Add a special message for false event handlers * Add missing whitespace * Revert size changes

Nic Bonetto committed Oct 31, 2017 at 06:02 UTC 544d5c720812c95131f7f572e401fadd3c65d27e
5 files changed +96 -27
packages/react-dom/src/__tests__/ReactDOMAttribute-test.js
+4 -3
@@ -54,8 +54,9 @@ describe('ReactDOM unknown attribute', () => {
54 expectDev(
55 normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
56 ).toMatch(
57 - 'Warning: Received `true` for non-boolean attribute `unknown`. ' +
58 - 'If this is expected, cast the value to a string.\n' +
57 + 'Received `true` for a non-boolean attribute `unknown`.\n\n' +
58 + 'If you want to write it to the DOM, pass a string instead: ' +
59 + 'unknown="true" or unknown={value.toString()}.\n' +
60 ' in div (at **)',
61 );
62 expectDev(console.error.calls.count()).toBe(1);
@@ -87,7 +88,7 @@ describe('ReactDOM unknown attribute', () => {
88 expectDev(
89 normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
90 ).toMatch(
90 - 'Warning: Received NaN for numeric attribute `unknown`. ' +
91 + 'Warning: Received NaN for the `unknown` attribute. ' +
92 'If this is expected, cast the value to a string.\n' +
93 ' in div (at **)',
94 );
packages/react-dom/src/__tests__/ReactDOMComponent-test.js
+16 -6
@@ -714,7 +714,7 @@ describe('ReactDOMComponent', () => {
714
715 expectDev(console.error.calls.count()).toBe(1);
716 expectDev(console.error.calls.argsFor(0)[0]).toContain(
717 - 'Received a `function` for string attribute `is`. If this is expected, cast ' +
717 + 'Received a `function` for a string attribute `is`. If this is expected, cast ' +
718 'the value to a string.',
719 );
720 });
@@ -2042,7 +2042,9 @@ describe('ReactDOMComponent', () => {
2042 expect(el.hasAttribute('whatever')).toBe(false);
2043
2044 expectDev(console.error.calls.argsFor(0)[0]).toContain(
2045 - 'Warning: Received `true` for non-boolean attribute `whatever`',
2045 + 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2046 + 'If you want to write it to the DOM, pass a string instead: ' +
2047 + 'whatever="true" or whatever={value.toString()}.',
2048 );
2049 });
2050
@@ -2055,7 +2057,9 @@ describe('ReactDOMComponent', () => {
2057 expect(el.hasAttribute('whatever')).toBe(false);
2058
2059 expectDev(console.error.calls.argsFor(0)[0]).toContain(
2058 - 'Warning: Received `true` for non-boolean attribute `whatever`',
2060 + 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2061 + 'If you want to write it to the DOM, pass a string instead: ' +
2062 + 'whatever="true" or whatever={value.toString()}.',
2063 );
2064 });
2065
@@ -2128,7 +2132,7 @@ describe('ReactDOMComponent', () => {
2132 expect(el.getAttribute('whatever')).toBe('NaN');
2133
2134 expectDev(console.error.calls.argsFor(0)[0]).toContain(
2131 - 'Warning: Received NaN for numeric attribute `whatever`. If this is ' +
2135 + 'Warning: Received NaN for the `whatever` attribute. If this is ' +
2136 'expected, cast the value to a string.\n in div',
2137 );
2138 });
@@ -2230,7 +2234,9 @@ describe('ReactDOMComponent', () => {
2234 expect(el.hasAttribute('whatever')).toBe(false);
2235
2236 expectDev(console.error.calls.argsFor(0)[0]).toContain(
2233 - 'Warning: Received `true` for non-boolean attribute `whatever`.',
2237 + 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2238 + 'If you want to write it to the DOM, pass a string instead: ' +
2239 + 'whatever="true" or whatever={value.toString()}.',
2240 );
2241 });
2242
@@ -2283,7 +2289,11 @@ describe('ReactDOMComponent', () => {
2289
2290 expectDev(console.error.calls.count()).toBe(1);
2291 expectDev(console.error.calls.argsFor(0)[0]).toContain(
2286 - 'Warning: Received `false` for non-boolean attribute `whatever`.',
2292 + 'Received `false` for a non-boolean attribute `whatever`.\n\n' +
2293 + 'If you want to write it to the DOM, pass a string instead: ' +
2294 + 'whatever="false" or whatever={value.toString()}.\n\n' +
2295 + 'If you used to conditionally omit it with whatever={condition && value}, ' +
2296 + 'pass whatever={condition ? value : undefined} instead.',
2297 );
2298 });
2299 });
packages/react-dom/src/__tests__/ReactDOMFiber-test.js
+19
@@ -945,6 +945,25 @@ describe('ReactDOMFiber', () => {
945 );
946 });
947
948 + it('should warn with a special message for `false` event listeners', () => {
949 + spyOn(console, 'error');
950 + class Example extends React.Component {
951 + render() {
952 + return <div onClick={false} />;
953 + }
954 + }
955 + ReactDOM.render(<Example />, container);
956 + expectDev(console.error.calls.count()).toBe(1);
957 + expectDev(
958 + normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
959 + ).toContain(
960 + 'Expected `onClick` listener to be a function, instead got `false`.\n\n' +
961 + 'If you used to conditionally omit it with onClick={condition && value}, ' +
962 + 'pass onClick={condition ? value : undefined} instead.\n',
963 + ' in div (at **)\n' + ' in Example (at **)',
964 + );
965 + });
966 +
967 it('should not update event handlers until commit', () => {
968 let ops = [];
969 const handlerA = () => ops.push('A');
packages/react-dom/src/client/ReactDOMFiberComponent.js
+20 -7
@@ -166,13 +166,26 @@ if (__DEV__) {
166 };
167
168 var warnForInvalidEventListener = function(registrationName, listener) {
169 - warning(
170 - false,
171 - 'Expected `%s` listener to be a function, instead got a value of `%s` type.%s',
172 - registrationName,
173 - typeof listener,
174 - getCurrentFiberStackAddendum(),
175 - );
169 + if (listener === false) {
170 + warning(
171 + false,
172 + 'Expected `%s` listener to be a function, instead got `false`.\n\n' +
173 + 'If you used to conditionally omit it with %s={condition && value}, ' +
174 + 'pass %s={condition ? value : undefined} instead.%s',
175 + registrationName,
176 + registrationName,
177 + registrationName,
178 + getCurrentFiberStackAddendum(),
179 + );
180 + } else {
181 + warning(
182 + false,
183 + 'Expected `%s` listener to be a function, instead got a value of `%s` type.%s',
184 + registrationName,
185 + typeof listener,
186 + getCurrentFiberStackAddendum(),
187 + );
188 + }
189 };
190
191 // Parse the HTML and read it back to normalize the HTML string so that it
packages/react-dom/src/shared/ReactDOMUnknownPropertyHook.js
+37 -11
@@ -124,7 +124,7 @@ if (__DEV__) {
124 ) {
125 warning(
126 false,
127 - 'Received a `%s` for string attribute `is`. If this is expected, cast ' +
127 + 'Received a `%s` for a string attribute `is`. If this is expected, cast ' +
128 'the value to a string.%s',
129 typeof value,
130 getStackAddendum(),
@@ -136,7 +136,7 @@ if (__DEV__) {
136 if (typeof value === 'number' && isNaN(value)) {
137 warning(
138 false,
139 - 'Received NaN for numeric attribute `%s`. If this is expected, cast ' +
139 + 'Received NaN for the `%s` attribute. If this is expected, cast ' +
140 'the value to a string.%s',
141 name,
142 getStackAddendum(),
@@ -179,15 +179,41 @@ if (__DEV__) {
179 return true;
180 }
181
182 - if (typeof value === 'boolean') {
183 - warning(
184 - DOMProperty.shouldAttributeAcceptBooleanValue(name),
185 - 'Received `%s` for non-boolean attribute `%s`. If this is expected, cast ' +
186 - 'the value to a string.%s',
187 - value,
188 - name,
189 - getStackAddendum(),
190 - );
182 + if (
183 + typeof value === 'boolean' &&
184 + !DOMProperty.shouldAttributeAcceptBooleanValue(name)
185 + ) {
186 + if (value) {
187 + warning(
188 + false,
189 + 'Received `%s` for a non-boolean attribute `%s`.\n\n' +
190 + 'If you want to write it to the DOM, pass a string instead: ' +
191 + '%s="%s" or %s={value.toString()}.%s',
192 + value,
193 + name,
194 + name,
195 + value,
196 + name,
197 + getStackAddendum(),
198 + );
199 + } else {
200 + warning(
201 + false,
202 + 'Received `%s` for a non-boolean attribute `%s`.\n\n' +
203 + 'If you want to write it to the DOM, pass a string instead: ' +
204 + '%s="%s" or %s={value.toString()}.\n\n' +
205 + 'If you used to conditionally omit it with %s={condition && value}, ' +
206 + 'pass %s={condition ? value : undefined} instead.%s',
207 + value,
208 + name,
209 + name,
210 + value,
211 + name,
212 + name,
213 + name,
214 + getStackAddendum(),
215 + );
216 + }
217 warnedProperties[name] = true;
218 return true;
219 }