diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 3ced96af295..c300abe3c82 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -1846,7 +1846,7 @@ describe('ReactDOMFizzServer', () => { }).toErrorDev( [ 'Warning: An error occurred during hydration. The server HTML was replaced with client content in
.', - 'Warning: Expected server HTML to contain a matching
in
.\n' + + 'Warning: Expected server HTML to contain a matching
in
.\n' + ' in div (at **)\n' + ' in App (at **)', ], @@ -1947,7 +1947,7 @@ describe('ReactDOMFizzServer', () => { }).toErrorDev( [ 'Warning: An error occurred during hydration. The server HTML was replaced with client content', - 'Warning: Expected server HTML to contain a matching
in
.\n' + + 'Warning: Expected server HTML to contain a matching
in
.\n' + ' in div (at **)\n' + ' in App (at **)', ], diff --git a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js index 697ce84fe7b..c83a36c84bf 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js @@ -361,9 +361,9 @@ describe('ReactDOMServerPartialHydration', () => { if (__DEV__) { expect(mockError.mock.calls[0]).toEqual([ - 'Warning: Expected server HTML to contain a matching <%s> in <%s>.%s', - 'div', + 'Warning: Expected server HTML to contain a matching <%s> in %s.%s', 'div', + '
', '\n' + ' in div (at **)\n' + ' in Component (at **)\n' + @@ -610,9 +610,9 @@ describe('ReactDOMServerPartialHydration', () => { } if (__DEV__) { expect(mockError).toHaveBeenCalledWith( - 'Warning: Did not expect server HTML to contain a <%s> in <%s>.%s', - 'span', - 'div', + 'Warning: Did not expect server HTML to contain a %s in %s.%s', + '', + '
', '\n' + ' in Suspense (at **)\n' + ' in div (at **)\n' + diff --git a/packages/react-dom/src/__tests__/ReactRenderDocument-test.js b/packages/react-dom/src/__tests__/ReactRenderDocument-test.js index 95b814b331d..79b5af85fab 100644 --- a/packages/react-dom/src/__tests__/ReactRenderDocument-test.js +++ b/packages/react-dom/src/__tests__/ReactRenderDocument-test.js @@ -222,7 +222,9 @@ describe('rendering React components at document', () => { // getTestDocument() has an extra that we didn't render. expect(() => ReactDOM.hydrate(, testDocument), - ).toErrorDev('Did not expect server HTML to contain a in .'); + ).toErrorDev( + 'Did not expect server HTML to contain a in .', + ); expect(testDocument.body.innerHTML).toBe('Hello world'); }); diff --git a/packages/react-dom/src/client/ReactDOMComponent.js b/packages/react-dom/src/client/ReactDOMComponent.js index 7f9b6ac47d8..9e74a5ccff0 100644 --- a/packages/react-dom/src/client/ReactDOMComponent.js +++ b/packages/react-dom/src/client/ReactDOMComponent.js @@ -62,7 +62,7 @@ import { shouldRemoveAttribute, } from '../shared/DOMProperty'; import assertValidProps from '../shared/assertValidProps'; -import {DOCUMENT_NODE} from '../shared/HTMLNodeType'; +import {DOCUMENT_NODE, ELEMENT_NODE} from '../shared/HTMLNodeType'; import isCustomComponent from '../shared/isCustomComponent'; import possibleStandardNames from '../shared/possibleStandardNames'; import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook'; @@ -100,6 +100,7 @@ let warnForInvalidEventListener; let canDiffStyleForHydrationWarning; let normalizeHTML; +let formatTagDEV; if (__DEV__) { warnedUnknownTags = { @@ -206,6 +207,31 @@ if (__DEV__) { testElement.innerHTML = html; return testElement.innerHTML; }; + + formatTagDEV = function(node: Element | Document | DocumentFragment): string { + let str = '<' + node.nodeName.toLowerCase(); + if (node.nodeType === ELEMENT_NODE) { + const element = ((node: any): Element); + const attributes = element.attributes; + for (let i = 0; i < attributes.length; i++) { + if (i > 30) { + str += ' ...'; + break; + } + const attributeName = attributes[i].name; + const value = attributes[i].value; + if (value != null) { + let trimmedValue = value; + if (value.length > 30) { + trimmedValue = value.substr(0, 30) + '...'; + } + str += ' ' + attributeName + '="' + trimmedValue + '"'; + } + } + } + str += '>'; + return str; + }; } // HTML parsing normalizes CR and CRLF to LF. @@ -1209,9 +1235,9 @@ export function warnForDeletedHydratableElement( } didWarnInvalidHydration = true; console.error( - 'Did not expect server HTML to contain a <%s> in <%s>.', - child.nodeName.toLowerCase(), - parentNode.nodeName.toLowerCase(), + 'Did not expect server HTML to contain a %s in %s.', + formatTagDEV(child), + formatTagDEV(parentNode), ); } } @@ -1226,9 +1252,9 @@ export function warnForDeletedHydratableText( } didWarnInvalidHydration = true; console.error( - 'Did not expect server HTML to contain the text node "%s" in <%s>.', + 'Did not expect server HTML to contain the text node "%s" in %s.', child.nodeValue, - parentNode.nodeName.toLowerCase(), + formatTagDEV(parentNode), ); } } @@ -1244,9 +1270,9 @@ export function warnForInsertedHydratedElement( } didWarnInvalidHydration = true; console.error( - 'Expected server HTML to contain a matching <%s> in <%s>.', + 'Expected server HTML to contain a matching <%s> in %s.', tag, - parentNode.nodeName.toLowerCase(), + formatTagDEV(parentNode), ); } } @@ -1268,9 +1294,9 @@ export function warnForInsertedHydratedText( } didWarnInvalidHydration = true; console.error( - 'Expected server HTML to contain a matching text node for "%s" in <%s>.', + 'Expected server HTML to contain a matching text node for "%s" in %s.', text, - parentNode.nodeName.toLowerCase(), + formatTagDEV(parentNode), ); } }