Skip to content

Commit 8888c46

Browse files
committed
Fix a warning issue uncovered by flat bundle testing
With flat bundles, we couldn't produce a good warning for <div onclick={}> on SSR because it doesn't use the event system. However the issue was not visible in normal Jest runs because the event plugins have been injected by the time the test ran. To solve this, I am explicitly passing whether event system is available as an argument to the hook. This makes the behavior consistent between source and bundle tests. Then I change the tests to document the actual logic and _attempt_ to show a nice message (e.g. we know for sure `onclick` is a bad event but we don't know the right name for it on the server so we just say a generic message about camelCase naming convention).
1 parent 4da70e7 commit 8888c46

File tree

4 files changed

+81
-57
lines changed

4 files changed

+81
-57
lines changed

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

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,15 +1709,25 @@ describe('ReactDOMComponent', () => {
17091709
it('should warn about incorrect casing on event handlers (ssr)', () => {
17101710
spyOnDev(console, 'error');
17111711
ReactDOMServer.renderToString(
1712-
React.createElement('input', {type: 'text', onclick: '1'}),
1712+
React.createElement('input', {type: 'text', oninput: '1'}),
17131713
);
17141714
ReactDOMServer.renderToString(
17151715
React.createElement('input', {type: 'text', onKeydown: '1'}),
17161716
);
17171717
if (__DEV__) {
1718-
expect(console.error.calls.count()).toBe(2);
1719-
expect(console.error.calls.argsFor(0)[0]).toContain('onClick');
1720-
expect(console.error.calls.argsFor(1)[0]).toContain('onKeyDown');
1718+
expect(console.error.calls.count()).toBe(1);
1719+
expect(console.error.calls.argsFor(0)[0]).toContain(
1720+
'Invalid event handler property `oninput`. ' +
1721+
'React events use the camelCase naming convention, ' +
1722+
// Note: we don't know the right event name so we
1723+
// use a generic one (onClick) as a suggestion.
1724+
// This is because we don't bundle the event system
1725+
// on the server.
1726+
'for example `onClick`.',
1727+
);
1728+
// We can't warn for `onKeydown` on the server because
1729+
// there is no way tell if this is a valid event or not
1730+
// without access to the event system (which we don't bundle).
17211731
}
17221732
});
17231733

@@ -1735,14 +1745,14 @@ describe('ReactDOMComponent', () => {
17351745
it('should warn about incorrect casing on event handlers', () => {
17361746
spyOnDev(console, 'error');
17371747
ReactTestUtils.renderIntoDocument(
1738-
React.createElement('input', {type: 'text', onclick: '1'}),
1748+
React.createElement('input', {type: 'text', oninput: '1'}),
17391749
);
17401750
ReactTestUtils.renderIntoDocument(
17411751
React.createElement('input', {type: 'text', onKeydown: '1'}),
17421752
);
17431753
if (__DEV__) {
17441754
expect(console.error.calls.count()).toBe(2);
1745-
expect(console.error.calls.argsFor(0)[0]).toContain('onClick');
1755+
expect(console.error.calls.argsFor(0)[0]).toContain('onInput');
17461756
expect(console.error.calls.argsFor(1)[0]).toContain('onKeyDown');
17471757
}
17481758
});
@@ -1860,15 +1870,20 @@ describe('ReactDOMComponent', () => {
18601870
it('gives source code refs for unknown prop warning (ssr)', () => {
18611871
spyOnDev(console, 'error');
18621872
ReactDOMServer.renderToString(<div class="paladin" />);
1863-
ReactDOMServer.renderToString(<input type="text" onclick="1" />);
1873+
ReactDOMServer.renderToString(<input type="text" oninput="1" />);
18641874
if (__DEV__) {
18651875
expect(console.error.calls.count()).toBe(2);
18661876
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
18671877
'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
18681878
);
18691879
expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1870-
'Warning: Invalid event handler property `onclick`. Did you mean ' +
1871-
'`onClick`?\n in input (at **)',
1880+
'Warning: Invalid event handler property `oninput`. ' +
1881+
// Note: we don't know the right event name so we
1882+
// use a generic one (onClick) as a suggestion.
1883+
// This is because we don't bundle the event system
1884+
// on the server.
1885+
'React events use the camelCase naming convention, for example `onClick`.' +
1886+
'\n in input (at **)',
18721887
);
18731888
}
18741889
});

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ if (__DEV__) {
6767
var validatePropertiesInDevelopment = function(type, props) {
6868
validateARIAProperties(type, props);
6969
validateInputProperties(type, props);
70-
validateUnknownProperties(type, props);
70+
validateUnknownProperties(type, props, /* canUseEventSystem */ true);
7171
};
7272

7373
// HTML parsing normalizes CR and CRLF to LF.

packages/react-dom/src/server/ReactPartialRenderer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ if (__DEV__) {
5454
var validatePropertiesInDevelopment = function(type, props) {
5555
validateARIAProperties(type, props);
5656
validateInputProperties(type, props);
57-
validateUnknownProperties(type, props);
57+
validateUnknownProperties(type, props, /* canUseEventSystem */ false);
5858
};
5959

6060
var describeStackFrame = function(element): string {

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

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import {
99
registrationNameModules,
10-
plugins,
1110
possibleRegistrationNames,
1211
} from 'events/EventPluginRegistry';
1312
import {ReactDebugCurrentFrame} from 'shared/ReactGlobalSharedState';
@@ -30,51 +29,72 @@ function getStackAddendum() {
3029
if (__DEV__) {
3130
var warnedProperties = {};
3231
var hasOwnProperty = Object.prototype.hasOwnProperty;
33-
var EVENT_NAME_REGEX = /^on[A-Z]/;
32+
var EVENT_NAME_REGEX = /^on./;
33+
var INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/;
3434
var rARIA = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$');
3535
var rARIACamel = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$');
3636

37-
var validateProperty = function(tagName, name, value) {
37+
var validateProperty = function(tagName, name, value, canUseEventSystem) {
3838
if (hasOwnProperty.call(warnedProperties, name) && warnedProperties[name]) {
3939
return true;
4040
}
4141

42-
if (registrationNameModules.hasOwnProperty(name)) {
43-
return true;
44-
}
45-
46-
if (plugins.length === 0 && EVENT_NAME_REGEX.test(name)) {
47-
// If no event plugins have been injected, we might be in a server environment.
48-
// Don't check events in this case.
49-
return true;
50-
}
51-
5242
var lowerCasedName = name.toLowerCase();
53-
var registrationName = possibleRegistrationNames.hasOwnProperty(
54-
lowerCasedName,
55-
)
56-
? possibleRegistrationNames[lowerCasedName]
57-
: null;
58-
59-
if (registrationName != null) {
43+
if (lowerCasedName === 'onfocusin' || lowerCasedName === 'onfocusout') {
6044
warning(
6145
false,
62-
'Invalid event handler property `%s`. Did you mean `%s`?%s',
63-
name,
64-
registrationName,
65-
getStackAddendum(),
46+
'React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' +
47+
'All React events are normalized to bubble, so onFocusIn and onFocusOut ' +
48+
'are not needed/supported by React.',
6649
);
6750
warnedProperties[name] = true;
6851
return true;
6952
}
7053

71-
if (lowerCasedName.indexOf('on') === 0 && lowerCasedName.length > 2) {
72-
warning(
73-
false,
74-
'Unknown event handler property `%s`. It will be ignored.%s',
75-
name,
76-
getStackAddendum(),
77-
);
54+
// We can't rely on the event system being injected on the server.
55+
if (canUseEventSystem) {
56+
if (registrationNameModules.hasOwnProperty(name)) {
57+
return true;
58+
}
59+
var registrationName = possibleRegistrationNames.hasOwnProperty(
60+
lowerCasedName,
61+
)
62+
? possibleRegistrationNames[lowerCasedName]
63+
: null;
64+
if (registrationName != null) {
65+
warning(
66+
false,
67+
'Invalid event handler property `%s`. Did you mean `%s`?%s',
68+
name,
69+
registrationName,
70+
getStackAddendum(),
71+
);
72+
warnedProperties[name] = true;
73+
return true;
74+
}
75+
if (EVENT_NAME_REGEX.test(name)) {
76+
warning(
77+
false,
78+
'Unknown event handler property `%s`. It will be ignored.%s',
79+
name,
80+
getStackAddendum(),
81+
);
82+
warnedProperties[name] = true;
83+
return true;
84+
}
85+
} else if (EVENT_NAME_REGEX.test(name)) {
86+
// If no event plugins have been injected, we are in a server environment.
87+
// So we can't tell if the event name is correct for sure, but we can filter
88+
// out known bad ones like `onclick`. We can't suggest a specific replacement though.
89+
if (INVALID_EVENT_NAME_REGEX.test(name)) {
90+
warning(
91+
false,
92+
'Invalid event handler property `%s`. ' +
93+
'React events use the camelCase naming convention, for example `onClick`.%s',
94+
name,
95+
getStackAddendum(),
96+
);
97+
}
7898
warnedProperties[name] = true;
7999
return true;
80100
}
@@ -84,17 +104,6 @@ if (__DEV__) {
84104
return true;
85105
}
86106

87-
if (lowerCasedName === 'onfocusin' || lowerCasedName === 'onfocusout') {
88-
warning(
89-
false,
90-
'React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' +
91-
'All React events are normalized to bubble, so onFocusIn and onFocusOut ' +
92-
'are not needed/supported by React.',
93-
);
94-
warnedProperties[name] = true;
95-
return true;
96-
}
97-
98107
if (lowerCasedName === 'innerhtml') {
99108
warning(
100109
false,
@@ -233,10 +242,10 @@ if (__DEV__) {
233242
};
234243
}
235244

236-
var warnUnknownProperties = function(type, props) {
245+
var warnUnknownProperties = function(type, props, canUseEventSystem) {
237246
var unknownProps = [];
238247
for (var key in props) {
239-
var isValid = validateProperty(type, key, props[key]);
248+
var isValid = validateProperty(type, key, props[key], canUseEventSystem);
240249
if (!isValid) {
241250
unknownProps.push(key);
242251
}
@@ -266,9 +275,9 @@ var warnUnknownProperties = function(type, props) {
266275
}
267276
};
268277

269-
export function validateProperties(type, props) {
278+
export function validateProperties(type, props, canUseEventSystem) {
270279
if (isCustomComponent(type, props)) {
271280
return;
272281
}
273-
warnUnknownProperties(type, props);
282+
warnUnknownProperties(type, props, canUseEventSystem);
274283
}

0 commit comments

Comments
 (0)