diff --git a/src/addons/transitions/ReactTransitionGroup.js b/src/addons/transitions/ReactTransitionGroup.js
index dd87ada1babae..55c5221af526d 100644
--- a/src/addons/transitions/ReactTransitionGroup.js
+++ b/src/addons/transitions/ReactTransitionGroup.js
@@ -256,9 +256,21 @@ var ReactTransitionGroup = React.createClass({
));
}
}
+
+ // Do not forward ReactTransitionGroup props to primitive DOM nodes
+ var props = Object.assign({}, this.props);
+ delete props.transitionLeave;
+ delete props.transitionName;
+ delete props.transitionAppear;
+ delete props.transitionEnter;
+ delete props.childFactory;
+ delete props.transitionLeaveTimeout;
+ delete props.transitionEnterTimeout;
+ delete props.component;
+
return React.createElement(
this.props.component,
- this.props,
+ props,
childrenToRender
);
},
diff --git a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
index a0c1db729800f..6c56ca1c2f468 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
@@ -19,6 +19,10 @@ describe('ReactDOMComponent', function() {
var ReactDOMServer;
var inputValueTracking;
+ function normalizeCodeLocInfo(str) {
+ return str.replace(/\(at .+?:\d+\)/g, '(at **)');
+ }
+
beforeEach(function() {
jest.resetModuleRegistry();
React = require('React');
@@ -148,6 +152,17 @@ describe('ReactDOMComponent', function() {
expect(console.error.argsForCall.length).toBe(2);
});
+ it('should warn for unknown prop', function() {
+ spyOn(console, 'error');
+ var container = document.createElement('div');
+ ReactDOM.render(
tag. Remove this prop from the element. ' +
+ 'For details, see https://fb.me/react-unknown-prop\n in div (at **)'
+ );
+ });
+
it('should warn about styles with numeric string values for non-unitless properties', function() {
spyOn(console, 'error');
@@ -1332,15 +1347,15 @@ describe('ReactDOMComponent', function() {
ReactDOMServer.renderToString(
);
expect(console.error.argsForCall.length).toBe(2);
expect(
- console.error.argsForCall[0][0].replace(/\(.+?:\d+\)/g, '(**:*)')
+ normalizeCodeLocInfo(console.error.argsForCall[0][0])
).toBe(
- 'Warning: Unknown DOM property class. Did you mean className? (**:*)'
+ 'Warning: Unknown DOM property class. Did you mean className?\n in div (at **)'
);
expect(
- console.error.argsForCall[1][0].replace(/\(.+?:\d+\)/g, '(**:*)')
+ normalizeCodeLocInfo(console.error.argsForCall[1][0])
).toBe(
'Warning: Unknown event handler property onclick. Did you mean ' +
- '`onClick`? (**:*)'
+ '`onClick`?\n in input (at **)'
);
});
@@ -1354,10 +1369,11 @@ describe('ReactDOMComponent', function() {
ReactDOMServer.renderToString(
, container);
expect(console.error.argsForCall.length).toBe(1);
expect(
- console.error.argsForCall[0][0].replace(/\(.+?:\d+\)/g, '(**:*)')
+ normalizeCodeLocInfo(console.error.argsForCall[0][0])
).toBe(
- 'Warning: Unknown DOM property class. Did you mean className? (**:*)'
+ 'Warning: Unknown DOM property class. Did you mean className?\n in div (at **)'
);
+
});
it('gives source code refs for unknown prop warning for exact elements ', function() {
@@ -1375,10 +1391,12 @@ describe('ReactDOMComponent', function() {
expect(console.error.argsForCall.length).toBe(2);
- var matches = console.error.argsForCall[0][0].match(/.*className.*\(.*:(\d+)\)/);
+ expect(console.error.argsForCall[0][0]).toContain('className');
+ var matches = console.error.argsForCall[0][0].match(/.*\(.*:(\d+)\).*/);
var previousLine = matches[1];
- matches = console.error.argsForCall[1][0].match(/.*onClick.*\(.*:(\d+)\)/);
+ expect(console.error.argsForCall[1][0]).toContain('onClick');
+ matches = console.error.argsForCall[1][0].match(/.*\(.*:(\d+)\).*/);
var currentLine = matches[1];
//verify line number has a proper relative difference,
@@ -1424,10 +1442,12 @@ describe('ReactDOMComponent', function() {
expect(console.error.argsForCall.length).toBe(2);
- var matches = console.error.argsForCall[0][0].match(/.*className.*\(.*:(\d+)\)/);
+ expect(console.error.argsForCall[0][0]).toContain('className');
+ var matches = console.error.argsForCall[0][0].match(/.*\(.*:(\d+)\).*/);
var previousLine = matches[1];
- matches = console.error.argsForCall[1][0].match(/.*onClick.*\(.*:(\d+)\)/);
+ expect(console.error.argsForCall[1][0]).toContain('onClick');
+ matches = console.error.argsForCall[1][0].match(/.*\(.*:(\d+)\).*/);
var currentLine = matches[1];
//verify line number has a proper relative difference,
diff --git a/src/renderers/dom/shared/devtools/ReactDOMUnknownPropertyDevtool.js b/src/renderers/dom/shared/devtools/ReactDOMUnknownPropertyDevtool.js
index d4ca0a449bb8c..d9eccd1da566c 100644
--- a/src/renderers/dom/shared/devtools/ReactDOMUnknownPropertyDevtool.js
+++ b/src/renderers/dom/shared/devtools/ReactDOMUnknownPropertyDevtool.js
@@ -13,6 +13,7 @@
var DOMProperty = require('DOMProperty');
var EventPluginRegistry = require('EventPluginRegistry');
+var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
var warning = require('warning');
@@ -22,10 +23,19 @@ if (__DEV__) {
dangerouslySetInnerHTML: true,
key: true,
ref: true,
+
+ defaultValue: true,
+ valueLink: true,
+ defaultChecked: true,
+ checkedLink: true,
+ innerHTML: true,
+ suppressContentEditableWarning: true,
+ onFocusIn: true,
+ onFocusOut: true,
};
var warnedProperties = {};
- var warnUnknownProperty = function(name, source) {
+ var warnUnknownProperty = function(tagName, name, debugID) {
if (DOMProperty.properties.hasOwnProperty(name) || DOMProperty.isCustomAttribute(name)) {
return;
}
@@ -48,16 +58,6 @@ if (__DEV__) {
null
);
- // 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,
- formatSource(source)
- );
-
var registrationName = (
EventPluginRegistry.possibleRegistrationNames.hasOwnProperty(
lowerCasedName
@@ -66,36 +66,56 @@ if (__DEV__) {
null
);
- warning(
- registrationName == null,
- 'Unknown event handler property %s. Did you mean `%s`? %s',
- name,
- registrationName,
- formatSource(source)
- );
- };
-
- var formatSource = function(source) {
- return source ? `(${source.fileName.replace(/^.*[\\\/]/, '')}:${source.lineNumber})` : '';
+ if (standardName != null) {
+ warning(
+ standardName == null,
+ 'Unknown DOM property %s. Did you mean %s?%s',
+ name,
+ standardName,
+ ReactComponentTreeDevtool.getStackAddendumByID(debugID)
+ );
+ } else if (registrationName != null) {
+ warning(
+ registrationName == null,
+ 'Unknown event handler property %s. Did you mean `%s`?%s',
+ name,
+ registrationName,
+ ReactComponentTreeDevtool.getStackAddendumByID(debugID)
+ );
+ } else {
+ // We were unable to guess which prop the user intended.
+ // It is likely that the user was just blindly spreading/forwarding props
+ // Components should be careful to only render valid props/attributes.
+ warning(
+ false,
+ 'Unknown prop `%s` on <%s> tag. Remove this prop from the element. ' +
+ 'For details, see https://fb.me/react-unknown-prop%s',
+ name,
+ tagName,
+ ReactComponentTreeDevtool.getStackAddendumByID(debugID)
+ );
+ }
};
-
}
-function handleElement(element) {
+function handleElement(debugID, element) {
if (element == null || typeof element.type !== 'string') {
return;
}
+ if (element.type.indexOf('-') >= 0 || element.props.is) {
+ return;
+ }
for (var key in element.props) {
- warnUnknownProperty(key, element._source);
+ warnUnknownProperty(element.type, key, debugID);
}
}
var ReactDOMUnknownPropertyDevtool = {
onBeforeMountComponent(debugID, element) {
- handleElement(element);
+ handleElement(debugID, element);
},
onBeforeUpdateComponent(debugID, element) {
- handleElement(element);
+ handleElement(debugID, element);
},
};