@samitouri / QOS-React-2 / commits / de1de9e18f

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