Skip to content

Commit d60089c

Browse files
committed
Improve validateDOMNesting message for whitespace
For #5071.
1 parent 921d8c1 commit d60089c

File tree

4 files changed

+41
-10
lines changed

4 files changed

+41
-10
lines changed

src/renderers/dom/client/validateDOMNesting.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,11 +322,24 @@ if (__DEV__) {
322322

323323
var didWarn = {};
324324

325-
validateDOMNesting = function(childTag, childInstance, ancestorInfo) {
325+
validateDOMNesting = function(
326+
childTag,
327+
childText,
328+
childInstance,
329+
ancestorInfo
330+
) {
326331
ancestorInfo = ancestorInfo || emptyAncestorInfo;
327332
var parentInfo = ancestorInfo.current;
328333
var parentTag = parentInfo && parentInfo.tag;
329334

335+
if (childText != null) {
336+
warning(
337+
childTag == null,
338+
'validateDOMNesting: when childText is passed, childTag should be null'
339+
);
340+
childTag = '#text';
341+
}
342+
330343
var invalidParent =
331344
isTagValidWithParent(childTag, parentTag) ? null : parentInfo;
332345
var invalidAncestor =
@@ -385,7 +398,17 @@ if (__DEV__) {
385398
didWarn[warnKey] = true;
386399

387400
var tagDisplayName = childTag;
388-
if (childTag !== '#text') {
401+
var whitespaceInfo = '';
402+
if (childTag === '#text') {
403+
if (/\S/.test(childText)) {
404+
tagDisplayName = 'Text nodes';
405+
} else {
406+
tagDisplayName = 'Whitespace text nodes';
407+
whitespaceInfo =
408+
' Make sure you don\'t have any extra whitespace between tags on ' +
409+
'each line of your source code.';
410+
}
411+
} else {
389412
tagDisplayName = '<' + childTag + '>';
390413
}
391414

@@ -398,10 +421,11 @@ if (__DEV__) {
398421
}
399422
warning(
400423
false,
401-
'validateDOMNesting(...): %s cannot appear as a child of <%s>. ' +
424+
'validateDOMNesting(...): %s cannot appear as a child of <%s>.%s ' +
402425
'See %s.%s',
403426
tagDisplayName,
404427
ancestorTag,
428+
whitespaceInfo,
405429
ownerInfo,
406430
info
407431
);

src/renderers/dom/shared/ReactDOMComponent.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ if (__DEV__) {
270270
return;
271271
}
272272

273+
validateDOMNesting(null, String(content), this, this._ancestorInfo);
273274
this._contentDebugID = contentDebugID;
274275
if (hasExistingContent) {
275276
ReactInstrumentation.debugTool.onBeforeUpdateComponent(contentDebugID, content);
@@ -605,7 +606,7 @@ ReactDOMComponent.Mixin = {
605606
if (parentInfo) {
606607
// parentInfo should always be present except for the top-level
607608
// component when server rendering
608-
validateDOMNesting(this._tag, this, parentInfo);
609+
validateDOMNesting(this._tag, null, this, parentInfo);
609610
}
610611
this._ancestorInfo =
611612
validateDOMNesting.updatedAncestorInfo(parentInfo, this._tag, this);

src/renderers/dom/shared/ReactDOMTextComponent.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ Object.assign(ReactDOMTextComponent.prototype, {
7575
if (parentInfo) {
7676
// parentInfo should always be present except for the top-level
7777
// component when server rendering
78-
validateDOMNesting('#text', this, parentInfo);
78+
validateDOMNesting(null, this._stringText, this, parentInfo);
7979
}
8080
}
8181

src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ describe('ReactDOMComponent', function() {
736736
});
737737

738738
it('should work error event on <source> element', function() {
739-
spyOn(console, 'error');
739+
spyOn(console, 'error');
740740
var container = document.createElement('div');
741741
ReactDOM.render(
742742
<video>
@@ -1225,7 +1225,7 @@ describe('ReactDOMComponent', function() {
12251225

12261226
class Row extends React.Component {
12271227
render() {
1228-
return <tr />;
1228+
return <tr>x</tr>;
12291229
}
12301230
}
12311231

@@ -1237,15 +1237,21 @@ describe('ReactDOMComponent', function() {
12371237

12381238
ReactTestUtils.renderIntoDocument(<Foo />);
12391239

1240-
expect(console.error.calls.count()).toBe(2);
1240+
expect(console.error.calls.count()).toBe(3);
12411241
expect(console.error.calls.argsFor(0)[0]).toBe(
12421242
'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
12431243
'<table>. See Foo > table > Row > tr. Add a <tbody> to your code to ' +
12441244
'match the DOM tree generated by the browser.'
12451245
);
12461246
expect(console.error.calls.argsFor(1)[0]).toBe(
1247-
'Warning: validateDOMNesting(...): #text cannot appear as a child ' +
1248-
'of <table>. See Foo > table > #text.'
1247+
'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
1248+
'child of <tr>. See Row > tr > #text.'
1249+
);
1250+
expect(console.error.calls.argsFor(2)[0]).toBe(
1251+
'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
1252+
'appear as a child of <table>. Make sure you don\'t have any extra ' +
1253+
'whitespace between tags on each line of your source code. See Foo > ' +
1254+
'table > #text.'
12491255
);
12501256
});
12511257

0 commit comments

Comments
 (0)