Add warning for unknown properties. (#6800)
Jim committed
May 25, 2016 at 06:58 UTC
de1de9e18f8faf153ad673c8206a2918e3e81035
3 files changed
+90
-38
src/addons/transitions/ReactTransitionGroup.js
+13
-1
@@ -256,9 +256,21 @@ var ReactTransitionGroup = React.createClass({
256
));
257
}
258
}
259
+
260
+ // Do not forward ReactTransitionGroup props to primitive DOM nodes
261
+ var props = Object.assign({}, this.props);
262
+ delete props.transitionLeave;
263
+ delete props.transitionName;
264
+ delete props.transitionAppear;
265
+ delete props.transitionEnter;
266
+ delete props.childFactory;
267
+ delete props.transitionLeaveTimeout;
268
+ delete props.transitionEnterTimeout;
269
+ delete props.component;
270
+
271
return React.createElement(
272
this.props.component,
261
- this.props,
273
+ props,
274
childrenToRender
275
);
276
},
src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
+30
-10
@@ -19,6 +19,10 @@ describe('ReactDOMComponent', function() {
19
var ReactDOMServer;
20
var inputValueTracking;
21
22
+ function normalizeCodeLocInfo(str) {
23
+ return str.replace(/\(at .+?:\d+\)/g, '(at **)');
24
+ }
25
+
26
beforeEach(function() {
27
jest.resetModuleRegistry();
28
React = require('React');
@@ -148,6 +152,17 @@ describe('ReactDOMComponent', function() {
152
expect(console.error.argsForCall.length).toBe(2);
153
});
154
155
+ it('should warn for unknown prop', function() {
156
+ spyOn(console, 'error');
157
+ var container = document.createElement('div');
158
+ ReactDOM.render(<div foo="bar" />, container);
159
+ expect(console.error.argsForCall.length).toBe(1);
160
+ expect(normalizeCodeLocInfo(console.error.argsForCall[0][0])).toBe(
161
+ 'Warning: Unknown prop `foo` on <div> tag. Remove this prop from the element. ' +
162
+ 'For details, see https://fb.me/react-unknown-prop\n in div (at **)'
163
+ );
164
+ });
165
+
166
it('should warn about styles with numeric string values for non-unitless properties', function() {
167
spyOn(console, 'error');
168
@@ -1334,15 +1349,15 @@ describe('ReactDOMComponent', function() {
1349
ReactDOMServer.renderToString(<input type="text" onclick="1"/>);
1350
expect(console.error.argsForCall.length).toBe(2);
1351
expect(
1337
- console.error.argsForCall[0][0].replace(/\(.+?:\d+\)/g, '(**:*)')
1352
+ normalizeCodeLocInfo(console.error.argsForCall[0][0])
1353
).toBe(
1339
- 'Warning: Unknown DOM property class. Did you mean className? (**:*)'
1354
+ 'Warning: Unknown DOM property class. Did you mean className?\n in div (at **)'
1355
);
1356
expect(
1342
- console.error.argsForCall[1][0].replace(/\(.+?:\d+\)/g, '(**:*)')
1357
+ normalizeCodeLocInfo(console.error.argsForCall[1][0])
1358
).toBe(
1359
'Warning: Unknown event handler property onclick. Did you mean ' +
1345
- '`onClick`? (**:*)'
1360
+ '`onClick`?\n in input (at **)'
1361
);
1362
});
1363
@@ -1356,10 +1371,11 @@ describe('ReactDOMComponent', function() {
1371
ReactDOMServer.renderToString(<div class="paladin" />, container);
1372
expect(console.error.argsForCall.length).toBe(1);
1373
expect(
1359
- console.error.argsForCall[0][0].replace(/\(.+?:\d+\)/g, '(**:*)')
1374
+ normalizeCodeLocInfo(console.error.argsForCall[0][0])
1375
).toBe(
1361
- 'Warning: Unknown DOM property class. Did you mean className? (**:*)'
1376
+ 'Warning: Unknown DOM property class. Did you mean className?\n in div (at **)'
1377
);
1378
+
1379
});
1380
1381
it('gives source code refs for unknown prop warning for exact elements ', function() {
@@ -1377,10 +1393,12 @@ describe('ReactDOMComponent', function() {
1393
1394
expect(console.error.argsForCall.length).toBe(2);
1395
1380
- var matches = console.error.argsForCall[0][0].match(/.*className.*\(.*:(\d+)\)/);
1396
+ expect(console.error.argsForCall[0][0]).toContain('className');
1397
+ var matches = console.error.argsForCall[0][0].match(/.*\(.*:(\d+)\).*/);
1398
var previousLine = matches[1];
1399
1383
- matches = console.error.argsForCall[1][0].match(/.*onClick.*\(.*:(\d+)\)/);
1400
+ expect(console.error.argsForCall[1][0]).toContain('onClick');
1401
+ matches = console.error.argsForCall[1][0].match(/.*\(.*:(\d+)\).*/);
1402
var currentLine = matches[1];
1403
1404
//verify line number has a proper relative difference,
@@ -1426,10 +1444,12 @@ describe('ReactDOMComponent', function() {
1444
1445
expect(console.error.argsForCall.length).toBe(2);
1446
1429
- var matches = console.error.argsForCall[0][0].match(/.*className.*\(.*:(\d+)\)/);
1447
+ expect(console.error.argsForCall[0][0]).toContain('className');
1448
+ var matches = console.error.argsForCall[0][0].match(/.*\(.*:(\d+)\).*/);
1449
var previousLine = matches[1];
1450
1432
- matches = console.error.argsForCall[1][0].match(/.*onClick.*\(.*:(\d+)\)/);
1451
+ expect(console.error.argsForCall[1][0]).toContain('onClick');
1452
+ matches = console.error.argsForCall[1][0].match(/.*\(.*:(\d+)\).*/);
1453
var currentLine = matches[1];
1454
1455
//verify line number has a proper relative difference,
src/renderers/dom/shared/devtools/ReactDOMUnknownPropertyDevtool.js
+47
-27
@@ -13,6 +13,7 @@
13
14
var DOMProperty = require('DOMProperty');
15
var EventPluginRegistry = require('EventPluginRegistry');
16
+var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
17
18
var warning = require('warning');
19
@@ -22,10 +23,19 @@ if (__DEV__) {
23
dangerouslySetInnerHTML: true,
24
key: true,
25
ref: true,
26
+
27
+ defaultValue: true,
28
+ valueLink: true,
29
+ defaultChecked: true,
30
+ checkedLink: true,
31
+ innerHTML: true,
32
+ suppressContentEditableWarning: true,
33
+ onFocusIn: true,
34
+ onFocusOut: true,
35
};
36
var warnedProperties = {};
37
28
- var warnUnknownProperty = function(name, source) {
38
+ var warnUnknownProperty = function(tagName, name, debugID) {
39
if (DOMProperty.properties.hasOwnProperty(name) || DOMProperty.isCustomAttribute(name)) {
40
return;
41
}
@@ -48,16 +58,6 @@ if (__DEV__) {
58
null
59
);
60
51
- // For now, only warn when we have a suggested correction. This prevents
52
- // logging too much when using transferPropsTo.
53
- warning(
54
- standardName == null,
55
- 'Unknown DOM property %s. Did you mean %s? %s',
56
- name,
57
- standardName,
58
- formatSource(source)
59
- );
60
-
61
var registrationName = (
62
EventPluginRegistry.possibleRegistrationNames.hasOwnProperty(
63
lowerCasedName
@@ -66,36 +66,56 @@ if (__DEV__) {
66
null
67
);
68
69
- warning(
70
- registrationName == null,
71
- 'Unknown event handler property %s. Did you mean `%s`? %s',
72
- name,
73
- registrationName,
74
- formatSource(source)
75
- );
76
- };
77
-
78
- var formatSource = function(source) {
79
- return source ? `(${source.fileName.replace(/^.*[\\\/]/, '')}:${source.lineNumber})` : '';
69
+ if (standardName != null) {
70
+ warning(
71
+ standardName == null,
72
+ 'Unknown DOM property %s. Did you mean %s?%s',
73
+ name,
74
+ standardName,
75
+ ReactComponentTreeDevtool.getStackAddendumByID(debugID)
76
+ );
77
+ } else if (registrationName != null) {
78
+ warning(
79
+ registrationName == null,
80
+ 'Unknown event handler property %s. Did you mean `%s`?%s',
81
+ name,
82
+ registrationName,
83
+ ReactComponentTreeDevtool.getStackAddendumByID(debugID)
84
+ );
85
+ } else {
86
+ // We were unable to guess which prop the user intended.
87
+ // It is likely that the user was just blindly spreading/forwarding props
88
+ // Components should be careful to only render valid props/attributes.
89
+ warning(
90
+ false,
91
+ 'Unknown prop `%s` on <%s> tag. Remove this prop from the element. ' +
92
+ 'For details, see https://fb.me/react-unknown-prop%s',
93
+ name,
94
+ tagName,
95
+ ReactComponentTreeDevtool.getStackAddendumByID(debugID)
96
+ );
97
+ }
98
};
81
-
99
}
100
84
-function handleElement(element) {
101
+function handleElement(debugID, element) {
102
if (element == null || typeof element.type !== 'string') {
103
return;
104
}
105
+ if (element.type.indexOf('-') >= 0 || element.props.is) {
106
+ return;
107
+ }
108
for (var key in element.props) {
89
- warnUnknownProperty(key, element._source);
109
+ warnUnknownProperty(element.type, key, debugID);
110
}
111
}
112
113
var ReactDOMUnknownPropertyDevtool = {
114
onBeforeMountComponent(debugID, element) {
95
- handleElement(element);
115
+ handleElement(debugID, element);
116
},
117
onBeforeUpdateComponent(debugID, element) {
98
- handleElement(element);
118
+ handleElement(debugID, element);
119
},
120
};
121