Skip to content

Commit 29f84bd

Browse files
committed
Revert "Move validation of text nesting into ReactDOMComponent (facebook#26594)"
This reverts commit ac43bf6.
1 parent 8b41858 commit 29f84bd

File tree

4 files changed

+64
-102
lines changed

4 files changed

+64
-102
lines changed

packages/react-dom-bindings/src/client/ReactDOMComponent.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ import {
4545
updateTextarea,
4646
restoreControlledTextareaState,
4747
} from './ReactDOMTextarea';
48-
import {validateTextNesting} from './validateDOMNesting';
4948
import {track} from './inputValueTracking';
5049
import setInnerHTML from './setInnerHTML';
5150
import setTextContent from './setTextContent';
@@ -280,9 +279,6 @@ function setProp(
280279
switch (key) {
281280
case 'children': {
282281
if (typeof value === 'string') {
283-
if (__DEV__) {
284-
validateTextNesting(value, tag);
285-
}
286282
// Avoid setting initial textContent when the text is empty. In IE11 setting
287283
// textContent on a <textarea> will cause the placeholder to not
288284
// show within the <textarea> until it has been focused and blurred again.
@@ -294,9 +290,6 @@ function setProp(
294290
setTextContent(domElement, value);
295291
}
296292
} else if (typeof value === 'number') {
297-
if (__DEV__) {
298-
validateTextNesting('' + value, tag);
299-
}
300293
const canSetTextContent = !enableHostSingletons || tag !== 'body';
301294
if (canSetTextContent) {
302295
setTextContent(domElement, '' + value);

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,7 @@ import {
5959
} from './ReactDOMComponent';
6060
import {getSelectionInformation, restoreSelection} from './ReactInputSelection';
6161
import setTextContent from './setTextContent';
62-
import {
63-
validateDOMNesting,
64-
validateTextNesting,
65-
updatedAncestorInfoDev,
66-
} from './validateDOMNesting';
62+
import {validateDOMNesting, updatedAncestorInfoDev} from './validateDOMNesting';
6763
import {
6864
isEnabled as ReactBrowserEventEmitterIsEnabled,
6965
setEnabled as ReactBrowserEventEmitterSetEnabled,
@@ -332,7 +328,18 @@ export function createInstance(
332328
if (__DEV__) {
333329
// TODO: take namespace into account when validating.
334330
const hostContextDev: HostContextDev = (hostContext: any);
335-
validateDOMNesting(type, hostContextDev.ancestorInfo);
331+
validateDOMNesting(type, null, hostContextDev.ancestorInfo);
332+
if (
333+
typeof props.children === 'string' ||
334+
typeof props.children === 'number'
335+
) {
336+
const string = '' + props.children;
337+
const ownAncestorInfo = updatedAncestorInfoDev(
338+
hostContextDev.ancestorInfo,
339+
type,
340+
);
341+
validateDOMNesting(null, string, ownAncestorInfo);
342+
}
336343
namespace = hostContextDev.namespace;
337344
} else {
338345
const hostContextProd: HostContextProd = (hostContext: any);
@@ -484,6 +491,21 @@ export function prepareUpdate(
484491
// TODO: Figure out how to validateDOMNesting when children turn into a string.
485492
return null;
486493
}
494+
if (__DEV__) {
495+
const hostContextDev = ((hostContext: any): HostContextDev);
496+
if (
497+
typeof newProps.children !== typeof oldProps.children &&
498+
(typeof newProps.children === 'string' ||
499+
typeof newProps.children === 'number')
500+
) {
501+
const string = '' + newProps.children;
502+
const ownAncestorInfo = updatedAncestorInfoDev(
503+
hostContextDev.ancestorInfo,
504+
type,
505+
);
506+
validateDOMNesting(null, string, ownAncestorInfo);
507+
}
508+
}
487509
return diffProperties(domElement, type, oldProps, newProps);
488510
}
489511

@@ -507,10 +529,7 @@ export function createTextInstance(
507529
): TextInstance {
508530
if (__DEV__) {
509531
const hostContextDev = ((hostContext: any): HostContextDev);
510-
const ancestor = hostContextDev.ancestorInfo.current;
511-
if (ancestor != null) {
512-
validateTextNesting(text, ancestor.tag);
513-
}
532+
validateDOMNesting(null, text, hostContextDev.ancestorInfo);
514533
}
515534
const textNode: TextInstance = getOwnerDocumentFromRootContainer(
516535
rootContainerInstance,
@@ -1737,7 +1756,7 @@ export function resolveSingletonInstance(
17371756
if (__DEV__) {
17381757
const hostContextDev = ((hostContext: any): HostContextDev);
17391758
if (validateDOMNestingDev) {
1740-
validateDOMNesting(type, hostContextDev.ancestorInfo);
1759+
validateDOMNesting(type, null, hostContextDev.ancestorInfo);
17411760
}
17421761
}
17431762
const ownerDocument = getOwnerDocumentFromRootContainer(

packages/react-dom-bindings/src/client/validateDOMNesting.js

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -434,14 +434,29 @@ function findInvalidAncestorForTag(
434434
const didWarn: {[string]: boolean} = {};
435435

436436
function validateDOMNesting(
437-
childTag: string,
437+
childTag: ?string,
438+
childText: ?string,
438439
ancestorInfo: AncestorInfoDev,
439440
): void {
440441
if (__DEV__) {
441442
ancestorInfo = ancestorInfo || emptyAncestorInfoDev;
442443
const parentInfo = ancestorInfo.current;
443444
const parentTag = parentInfo && parentInfo.tag;
444445

446+
if (childText != null) {
447+
if (childTag != null) {
448+
console.error(
449+
'validateDOMNesting: when childText is passed, childTag should be null',
450+
);
451+
}
452+
childTag = '#text';
453+
} else if (childTag == null) {
454+
console.error(
455+
'validateDOMNesting: when childText or childTag must be provided',
456+
);
457+
return;
458+
}
459+
445460
const invalidParent = isTagValidWithParent(childTag, parentTag)
446461
? null
447462
: parentInfo;
@@ -463,7 +478,21 @@ function validateDOMNesting(
463478
}
464479
didWarn[warnKey] = true;
465480

466-
const tagDisplayName = '<' + childTag + '>';
481+
let tagDisplayName = childTag;
482+
let whitespaceInfo = '';
483+
if (childTag === '#text') {
484+
if (childText != null && /\S/.test(childText)) {
485+
tagDisplayName = 'Text nodes';
486+
} else {
487+
tagDisplayName = 'Whitespace text nodes';
488+
whitespaceInfo =
489+
" Make sure you don't have any extra whitespace between tags on " +
490+
'each line of your source code.';
491+
}
492+
} else {
493+
tagDisplayName = '<' + childTag + '>';
494+
}
495+
467496
if (invalidParent) {
468497
let info = '';
469498
if (ancestorTag === 'table' && childTag === 'tr') {
@@ -472,9 +501,10 @@ function validateDOMNesting(
472501
'the browser.';
473502
}
474503
console.error(
475-
'validateDOMNesting(...): %s cannot appear as a child of <%s>.%s',
504+
'validateDOMNesting(...): %s cannot appear as a child of <%s>.%s%s',
476505
tagDisplayName,
477506
ancestorTag,
507+
whitespaceInfo,
478508
info,
479509
);
480510
} else {
@@ -488,33 +518,4 @@ function validateDOMNesting(
488518
}
489519
}
490520

491-
function validateTextNesting(childText: string, parentTag: string): void {
492-
if (__DEV__) {
493-
if (isTagValidWithParent('#text', parentTag)) {
494-
return;
495-
}
496-
497-
// eslint-disable-next-line react-internal/safe-string-coercion
498-
const warnKey = '#text|' + parentTag;
499-
if (didWarn[warnKey]) {
500-
return;
501-
}
502-
didWarn[warnKey] = true;
503-
504-
if (/\S/.test(childText)) {
505-
console.error(
506-
'validateDOMNesting(...): Text nodes cannot appear as a child of <%s>.',
507-
parentTag,
508-
);
509-
} else {
510-
console.error(
511-
'validateDOMNesting(...): Whitespace text nodes cannot appear as a child of <%s>. ' +
512-
"Make sure you don't have any extra whitespace between tags on " +
513-
'each line of your source code.',
514-
parentTag,
515-
);
516-
}
517-
}
518-
}
519-
520-
export {updatedAncestorInfoDev, validateDOMNesting, validateTextNesting};
521+
export {updatedAncestorInfoDev, validateDOMNesting};

packages/react-dom/src/__tests__/ReactDOMComponent-test.js

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,57 +1855,6 @@ describe('ReactDOMComponent', () => {
18551855
]);
18561856
});
18571857

1858-
it('warns nicely for updating table rows to use text', () => {
1859-
const container = document.createElement('div');
1860-
1861-
function Row({children}) {
1862-
return <tr>{children}</tr>;
1863-
}
1864-
1865-
function Foo({children}) {
1866-
return <table>{children}</table>;
1867-
}
1868-
1869-
// First is fine.
1870-
ReactDOM.render(<Foo />, container);
1871-
1872-
expect(() => ReactDOM.render(<Foo> </Foo>, container)).toErrorDev([
1873-
'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
1874-
"appear as a child of <table>. Make sure you don't have any extra " +
1875-
'whitespace between tags on each line of your source code.' +
1876-
'\n in table (at **)' +
1877-
'\n in Foo (at **)',
1878-
]);
1879-
1880-
ReactDOM.render(
1881-
<Foo>
1882-
<tbody>
1883-
<Row />
1884-
</tbody>
1885-
</Foo>,
1886-
container,
1887-
);
1888-
1889-
expect(() =>
1890-
ReactDOM.render(
1891-
<Foo>
1892-
<tbody>
1893-
<Row>text</Row>
1894-
</tbody>
1895-
</Foo>,
1896-
container,
1897-
),
1898-
).toErrorDev([
1899-
'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
1900-
'child of <tr>.' +
1901-
'\n in tr (at **)' +
1902-
'\n in Row (at **)' +
1903-
'\n in tbody (at **)' +
1904-
'\n in table (at **)' +
1905-
'\n in Foo (at **)',
1906-
]);
1907-
});
1908-
19091858
it('gives useful context in warnings', () => {
19101859
function Row() {
19111860
return <tr />;

0 commit comments

Comments
 (0)