Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1275,14 +1275,14 @@ describe('ReactDOMComponent', function() {
);
});

it('should warn about incorrect casing on properties', function() {
it('should warn about incorrect casing on properties (ssr)', function() {
spyOn(console, 'error');
ReactDOMServer.renderToString(React.createElement('input', {type: 'text', tabindex: '1'}));
expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain('tabIndex');
});

it('should warn about incorrect casing on event handlers', function() {
it('should warn about incorrect casing on event handlers (ssr)', function() {
spyOn(console, 'error');
ReactDOMServer.renderToString(React.createElement('input', {type: 'text', onclick: '1'}));
ReactDOMServer.renderToString(React.createElement('input', {type: 'text', onKeydown: '1'}));
Expand All @@ -1291,6 +1291,22 @@ describe('ReactDOMComponent', function() {
expect(console.error.argsForCall[1][0]).toContain('onKeyDown');
});

it('should warn about incorrect casing on properties', function() {
spyOn(console, 'error');
ReactTestUtils.renderIntoDocument(React.createElement('input', {type: 'text', tabindex: '1'}));
expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain('tabIndex');
});

it('should warn about incorrect casing on event handlers', function() {
spyOn(console, 'error');
ReactTestUtils.renderIntoDocument(React.createElement('input', {type: 'text', onclick: '1'}));
ReactTestUtils.renderIntoDocument(React.createElement('input', {type: 'text', onKeydown: '1'}));
expect(console.error.argsForCall.length).toBe(2);
expect(console.error.argsForCall[0][0]).toContain('onClick');
expect(console.error.argsForCall[1][0]).toContain('onKeyDown');
});

it('should warn about class', function() {
spyOn(console, 'error');
ReactDOMServer.renderToString(React.createElement('div', {class: 'muffins'}));
Expand Down
44 changes: 21 additions & 23 deletions src/renderers/dom/shared/devtools/ReactDOMUnknownPropertyDevtool.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@

var DOMProperty = require('DOMProperty');
var EventPluginRegistry = require('EventPluginRegistry');
var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');

var warning = require('warning');

if (__DEV__) {
var lastDebugID;
var reactProps = {
children: true,
dangerouslySetInnerHTML: true,
Expand All @@ -27,15 +25,17 @@ if (__DEV__) {
};
var warnedProperties = {};

var warnUnknownProperty = function(name) {
var warnUnknownProperty = function(name, source) {
if (DOMProperty.properties.hasOwnProperty(name) || DOMProperty.isCustomAttribute(name)) {
return;
}
if (reactProps.hasOwnProperty(name) && reactProps[name] ||
warnedProperties.hasOwnProperty(name) && warnedProperties[name]) {
return;
}

if (EventPluginRegistry.registrationNameModules.hasOwnProperty(name)) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this necessary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because previously we were processing only non-event properties because we were listening in setValueForProperty or whatever, but now we are just reading off the element so we need to filter out the events ourselves.

return;
}
warnedProperties[name] = true;
var lowerCasedName = name.toLowerCase();

Expand All @@ -48,18 +48,14 @@ if (__DEV__) {
null
);

var source = ReactComponentTreeDevtool.getSource(lastDebugID);
var formattedSource = source ?
`(${source.fileName.replace(/^.*[\\\/]/, '')}:${source.lineNumber})` : '';

// For now, only warn when we have a suggested correction. This prevents
// logging too much when using transferPropsTo.
warning(
standardName == null,
'Unknown DOM property %s. Did you mean %s? %s',
name,
standardName,
formattedSource
formatSource(source)
);

var registrationName = (
Expand All @@ -75,29 +71,31 @@ if (__DEV__) {
'Unknown event handler property %s. Did you mean `%s`? %s',
name,
registrationName,
formattedSource
formatSource(source)
);
};

var formatSource = function(source) {
return source ? `(${source.fileName.replace(/^.*[\\\/]/, '')}:${source.lineNumber})` : '';
};

}

function handleElement(element) {
if (element == null || typeof element.type !== 'string') {
return;
}
for (var key in element.props) {
warnUnknownProperty(key, element._source);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, did this change make it so it does warn on non-SSR?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this devtool I mean.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this fixed the issue because the prop names were being filtered prior to this event firing, which means we only fire this event on props that are "correct". This is a reasonable behavior, because some devtools (eg. perf) might want to know about the DOM operations that are actually being performed, rather than every prop that was specified. We can re-think how/when those events fire, but that's a much longer discussion.

}
}

var ReactDOMUnknownPropertyDevtool = {
onCreateMarkupForProperty(name, value) {
warnUnknownProperty(name);
},
onSetValueForProperty(node, name, value) {
warnUnknownProperty(name);
},
onDeleteValueForProperty(node, name) {
warnUnknownProperty(name);
},
onBeforeMountComponent(debugID, element) {
// TODO: This currently assumes that properties are all set before recursing
// and mounting children, which needn't be the case in the future.
lastDebugID = debugID;
handleElement(element);
},
onBeforeUpdateComponent(debugID, element) {
lastDebugID = debugID;
handleElement(element);
},
};

Expand Down