diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js
index 07a313d9ec..47b164e9aa 100644
--- a/src/renderers/dom/shared/ReactDOMComponent.js
+++ b/src/renderers/dom/shared/ReactDOMComponent.js
@@ -170,6 +170,32 @@ function legacyReplaceProps(partialProps, callback) {
}
}
+function friendlyStringify(obj) {
+ if (typeof obj === 'object') {
+ if (Array.isArray(obj)) {
+ return '[' + obj.map(friendlyStringify).join(', ') + ']';
+ } else {
+ var pairs = [];
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
+ var keyEscaped = /^[a-z$_][\w$_]*$/i.test(key) ?
+ key :
+ JSON.stringify(key);
+ pairs.push(keyEscaped + ': ' + friendlyStringify(obj[key]));
+ }
+ }
+ return '{' + pairs.join(', ') + '}';
+ }
+ } else if (typeof obj === 'string') {
+ return JSON.stringify(obj);
+ } else if (typeof obj === 'function') {
+ return '[function object]';
+ }
+ // Differs from JSON.stringify in that undefined becauses undefined and that
+ // inf and nan don't become null
+ return String(obj);
+}
+
var styleMutationWarning = {};
function checkAndWarnForMutatedStyle(style1, style2, component) {
@@ -202,8 +228,8 @@ function checkAndWarnForMutatedStyle(style1, style2, component) {
'the `render` %s. Previous style: %s. Mutated style: %s.',
componentName,
owner ? 'of `' + ownerName + '`' : 'using <' + componentName + '>',
- JSON.stringify(style1),
- JSON.stringify(style2)
+ friendlyStringify(style1),
+ friendlyStringify(style2)
);
}
diff --git a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
index afabd5c8dc..22b10f663d 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
@@ -128,8 +128,8 @@ describe('ReactDOMComponent', function() {
'Warning: `div` was passed a style object that has previously been ' +
'mutated. Mutating `style` is deprecated. Consider cloning it ' +
'beforehand. Check the `render` of `App`. Previous style: ' +
- '{"border":"1px solid black"}. Mutated style: ' +
- '{"border":"1px solid black","position":"absolute"}.'
+ '{border: "1px solid black"}. Mutated style: ' +
+ '{border: "1px solid black", position: "absolute"}.'
);
style = {background: 'red'};
@@ -147,6 +147,23 @@ describe('ReactDOMComponent', function() {
expect(console.error.argsForCall.length).toBe(2);
});
+ it('should warn semi-nicely about NaN in style', function() {
+ spyOn(console, 'error');
+
+ var style = {fontSize: NaN};
+ var div = document.createElement('div');
+ ReactDOM.render(, div);
+ ReactDOM.render(, div);
+
+ expect(console.error.argsForCall.length).toBe(1);
+ expect(console.error.argsForCall[0][0]).toEqual(
+ 'Warning: `span` was passed a style object that has previously been ' +
+ 'mutated. Mutating `style` is deprecated. Consider cloning it ' +
+ 'beforehand. Check the `render` using . Previous style: ' +
+ '{fontSize: NaN}. Mutated style: {fontSize: NaN}.'
+ );
+ });
+
it('should update styles if initially null', function() {
var styles = null;
var container = document.createElement('div');