Skip to content

Commit 544d5c7

Browse files
NicBonettogaearon
authored andcommitted
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
1 parent 3f1f3dc commit 544d5c7

File tree

5 files changed

+96
-27
lines changed

5 files changed

+96
-27
lines changed

packages/react-dom/src/__tests__/ReactDOMAttribute-test.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ describe('ReactDOM unknown attribute', () => {
5454
expectDev(
5555
normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
5656
).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' +
5960
' in div (at **)',
6061
);
6162
expectDev(console.error.calls.count()).toBe(1);
@@ -87,7 +88,7 @@ describe('ReactDOM unknown attribute', () => {
8788
expectDev(
8889
normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
8990
).toMatch(
90-
'Warning: Received NaN for numeric attribute `unknown`. ' +
91+
'Warning: Received NaN for the `unknown` attribute. ' +
9192
'If this is expected, cast the value to a string.\n' +
9293
' in div (at **)',
9394
);

packages/react-dom/src/__tests__/ReactDOMComponent-test.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ describe('ReactDOMComponent', () => {
714714

715715
expectDev(console.error.calls.count()).toBe(1);
716716
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 ' +
718718
'the value to a string.',
719719
);
720720
});
@@ -2042,7 +2042,9 @@ describe('ReactDOMComponent', () => {
20422042
expect(el.hasAttribute('whatever')).toBe(false);
20432043

20442044
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()}.',
20462048
);
20472049
});
20482050

@@ -2055,7 +2057,9 @@ describe('ReactDOMComponent', () => {
20552057
expect(el.hasAttribute('whatever')).toBe(false);
20562058

20572059
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()}.',
20592063
);
20602064
});
20612065

@@ -2128,7 +2132,7 @@ describe('ReactDOMComponent', () => {
21282132
expect(el.getAttribute('whatever')).toBe('NaN');
21292133

21302134
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 ' +
21322136
'expected, cast the value to a string.\n in div',
21332137
);
21342138
});
@@ -2230,7 +2234,9 @@ describe('ReactDOMComponent', () => {
22302234
expect(el.hasAttribute('whatever')).toBe(false);
22312235

22322236
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()}.',
22342240
);
22352241
});
22362242

@@ -2283,7 +2289,11 @@ describe('ReactDOMComponent', () => {
22832289

22842290
expectDev(console.error.calls.count()).toBe(1);
22852291
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.',
22872297
);
22882298
});
22892299
});

packages/react-dom/src/__tests__/ReactDOMFiber-test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,25 @@ describe('ReactDOMFiber', () => {
945945
);
946946
});
947947

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+
948967
it('should not update event handlers until commit', () => {
949968
let ops = [];
950969
const handlerA = () => ops.push('A');

packages/react-dom/src/client/ReactDOMFiberComponent.js

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,26 @@ if (__DEV__) {
166166
};
167167

168168
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+
}
176189
};
177190

178191
// Parse the HTML and read it back to normalize the HTML string so that it

packages/react-dom/src/shared/ReactDOMUnknownPropertyHook.js

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ if (__DEV__) {
124124
) {
125125
warning(
126126
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 ' +
128128
'the value to a string.%s',
129129
typeof value,
130130
getStackAddendum(),
@@ -136,7 +136,7 @@ if (__DEV__) {
136136
if (typeof value === 'number' && isNaN(value)) {
137137
warning(
138138
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 ' +
140140
'the value to a string.%s',
141141
name,
142142
getStackAddendum(),
@@ -179,15 +179,41 @@ if (__DEV__) {
179179
return true;
180180
}
181181

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+
}
191217
warnedProperties[name] = true;
192218
return true;
193219
}

0 commit comments

Comments
 (0)