diff --git a/fixtures/ssr/src/components/SSRMismatchTest.js b/fixtures/ssr/src/components/SSRMismatchTest.js index 22113477989d8..74918fa2cccae 100644 --- a/fixtures/ssr/src/components/SSRMismatchTest.js +++ b/fixtures/ssr/src/components/SSRMismatchTest.js @@ -196,14 +196,38 @@ const testCases = [ { key: 'ssr-warnForInsertedHydratedElement-didNotFindHydratableInstance', renderServer: () => ( -
- SSRMismatchTest default text +
+ 1 + 2 + 3 + 4 + 5 + 6 + SSRMismatchTest default text + 7 + 8 + 9 + 10 + 11 + 12
), renderBrowser: () => ( // The inner element type is different from the server render, but the inner text is the same. -
-

SSRMismatchTest default text

+
+ 1 + 2 + 3 + 4 + 5 + 6 + SSRMismatchTest default text + 7 + 8 + 9 + 10 + 11 + 12
), }, diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js index a2576e2e706d7..839770cbff379 100644 --- a/packages/react-dom/src/client/ReactDOM.js +++ b/packages/react-dom/src/client/ReactDOM.js @@ -948,7 +948,7 @@ const DOMRenderer = ReactFiberReconciler({ props: Props, ) { if (__DEV__) { - warnForInsertedHydratedElement(parentContainer, type, props); + warnForInsertedHydratedElement(parentContainer, type, props, 0); } }, @@ -967,9 +967,10 @@ const DOMRenderer = ReactFiberReconciler({ parentInstance: Instance, type: string, props: Props, + index: number, ) { if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) { - warnForInsertedHydratedElement(parentInstance, type, props); + warnForInsertedHydratedElement(parentInstance, type, props, index); } }, diff --git a/packages/react-dom/src/client/ReactDOMFiberComponent.js b/packages/react-dom/src/client/ReactDOMFiberComponent.js index 59d1e248bb35b..1169ea429507e 100644 --- a/packages/react-dom/src/client/ReactDOMFiberComponent.js +++ b/packages/react-dom/src/client/ReactDOMFiberComponent.js @@ -1146,17 +1146,64 @@ export function warnForInsertedHydratedElement( parentNode: Element | Document, tag: string, props: Object, + index: number, ) { if (__DEV__) { if (didWarnInvalidHydration) { return; } didWarnInvalidHydration = true; + let htmlContext = []; + const ic = parentNode.childNodes.length; + const parentNodeName = parentNode.nodeName.toLowerCase(); + htmlContext.push( + ' <' + + parentNodeName + + (parentNode.className + ? ' className="' + parentNode.className + '"' + : '') + + '>', + ); + if (index - 5 > 0) { + htmlContext.push(' …'); + } + for (let i = index - 5; i <= index + 5; ++i) { + if (i >= 0 && i < ic) { + const childNode = parentNode.childNodes[i]; + const childNodeName = childNode.nodeName.toLowerCase(); + htmlContext.push( + (i === index ? '- ' : ' ') + + (childNode.nodeType === 1 + ? '<' + + childNodeName + + (childNode.className + ? ' className="' + childNode.className + '"' + : '') + + (childNode.textContent + ? '>' + childNode.textContent + '' + : ' />') + : childNode.textContent), + ); + if (i === index) { + htmlContext.push( + '+ <' + + tag + + (props.className ? ' className="' + props.className + '"' : '') + + ' />', + ); + } + } + } + if (index + 5 < ic - 1) { + htmlContext.push(' …'); + } + htmlContext.push(' '); warning( false, - 'Expected server HTML to contain a matching <%s> in <%s>.%s', + 'Expected server HTML to contain a matching <%s> in <%s>.%s%s', tag, - parentNode.nodeName.toLowerCase(), + parentNodeName, + '\n' + htmlContext.join('\n') + '\n', getStack(), ); } diff --git a/packages/react-reconciler/src/ReactFiberHydrationContext.js b/packages/react-reconciler/src/ReactFiberHydrationContext.js index bd3889580acf8..ccad8bab0c525 100644 --- a/packages/react-reconciler/src/ReactFiberHydrationContext.js +++ b/packages/react-reconciler/src/ReactFiberHydrationContext.js @@ -168,6 +168,7 @@ export default function( parentInstance, type, props, + fiber.index, ); break; case HostText: diff --git a/packages/react-reconciler/src/ReactFiberReconciler.js b/packages/react-reconciler/src/ReactFiberReconciler.js index 5ad78944fb50f..df37f139431ba 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.js @@ -206,6 +206,7 @@ type HydrationHostConfig = { parentInstance: I, type: T, props: P, + index: number, ): void, didNotFindHydratableTextInstance( parentType: T, diff --git a/scripts/rollup/results.json b/scripts/rollup/results.json index c59a7b3714d36..c7a28c0e8d8a3 100644 --- a/scripts/rollup/results.json +++ b/scripts/rollup/results.json @@ -46,22 +46,22 @@ "filename": "react-dom.development.js", "bundleType": "UMD_DEV", "packageName": "react-dom", - "size": 591567, - "gzip": 138758 + "size": 592727, + "gzip": 139090 }, { "filename": "react-dom.production.min.js", "bundleType": "UMD_PROD", "packageName": "react-dom", - "size": 96778, - "gzip": 31445 + "size": 96782, + "gzip": 31444 }, { "filename": "react-dom.development.js", "bundleType": "NODE_DEV", "packageName": "react-dom", - "size": 575580, - "gzip": 134533 + "size": 576740, + "gzip": 134864 }, { "filename": "react-dom.production.min.js", @@ -74,8 +74,8 @@ "filename": "ReactDOM-dev.js", "bundleType": "FB_DEV", "packageName": "react-dom", - "size": 594873, - "gzip": 136788 + "size": 596363, + "gzip": 137178 }, { "filename": "ReactDOM-prod.js", @@ -221,8 +221,8 @@ "filename": "react-art.development.js", "bundleType": "UMD_DEV", "packageName": "react-art", - "size": 389869, - "gzip": 86413 + "size": 389882, + "gzip": 86423 }, { "filename": "react-art.production.min.js", @@ -235,8 +235,8 @@ "filename": "react-art.development.js", "bundleType": "NODE_DEV", "packageName": "react-art", - "size": 313942, - "gzip": 67385 + "size": 313955, + "gzip": 67392 }, { "filename": "react-art.production.min.js", @@ -249,8 +249,8 @@ "filename": "ReactART-dev.js", "bundleType": "FB_DEV", "packageName": "react-art", - "size": 318024, - "gzip": 66603 + "size": 318053, + "gzip": 66612 }, { "filename": "ReactART-prod.js", @@ -263,8 +263,8 @@ "filename": "ReactNativeRenderer-dev.js", "bundleType": "RN_DEV", "packageName": "react-native-renderer", - "size": 443941, - "gzip": 97414 + "size": 443970, + "gzip": 97423 }, { "filename": "ReactNativeRenderer-prod.js", @@ -277,8 +277,8 @@ "filename": "react-test-renderer.development.js", "bundleType": "NODE_DEV", "packageName": "react-test-renderer", - "size": 311062, - "gzip": 66359 + "size": 311075, + "gzip": 66366 }, { "filename": "react-test-renderer.production.min.js", @@ -291,8 +291,8 @@ "filename": "ReactTestRenderer-dev.js", "bundleType": "FB_DEV", "packageName": "react-test-renderer", - "size": 315158, - "gzip": 65549 + "size": 315187, + "gzip": 65558 }, { "filename": "react-test-renderer-shallow.development.js", @@ -333,8 +333,8 @@ "filename": "react-reconciler.development.js", "bundleType": "NODE_DEV", "packageName": "react-reconciler", - "size": 292377, - "gzip": 61765 + "size": 292390, + "gzip": 61771 }, { "filename": "react-reconciler.production.min.js", @@ -375,8 +375,8 @@ "filename": "ReactFabric-dev.js", "bundleType": "RN_DEV", "packageName": "react-native-renderer", - "size": 438218, - "gzip": 96267 + "size": 438247, + "gzip": 96276 }, { "filename": "ReactFabric-prod.js", @@ -389,8 +389,8 @@ "filename": "react-reconciler-persistent.development.js", "bundleType": "NODE_DEV", "packageName": "react-reconciler", - "size": 291949, - "gzip": 61587 + "size": 291962, + "gzip": 61594 }, { "filename": "react-reconciler-persistent.production.min.js",