fix(hydration): log hydration error even when using async components (#9403)

close #9369
This commit is contained in:
Rodrigo Pimentel 2024-06-07 08:45:16 +01:00 committed by GitHub
parent aa156ed5c4
commit 5afc76c229
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 22 additions and 13 deletions

View File

@ -50,7 +50,15 @@ enum DOMNodeTypes {
COMMENT = 8, COMMENT = 8,
} }
let hasMismatch = false let hasLoggedMismatchError = false
const logMismatchError = () => {
if (__TEST__ || hasLoggedMismatchError) {
return
}
// this error should show up in production
console.error('Hydration completed but contains mismatches.')
hasLoggedMismatchError = true
}
const isSVGContainer = (container: Element) => const isSVGContainer = (container: Element) =>
container.namespaceURI!.includes('svg') && container.namespaceURI!.includes('svg') &&
@ -102,14 +110,10 @@ export function createHydrationFunctions(
container._vnode = vnode container._vnode = vnode
return return
} }
hasMismatch = false
hydrateNode(container.firstChild!, vnode, null, null, null) hydrateNode(container.firstChild!, vnode, null, null, null)
flushPostFlushCbs() flushPostFlushCbs()
container._vnode = vnode container._vnode = vnode
if (hasMismatch && !__TEST__) {
// this error should show up in production
console.error(`Hydration completed but contains mismatches.`)
}
} }
const hydrateNode = ( const hydrateNode = (
@ -170,7 +174,6 @@ export function createHydrationFunctions(
} }
} else { } else {
if ((node as Text).data !== vnode.children) { if ((node as Text).data !== vnode.children) {
hasMismatch = true
;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
warn( warn(
`Hydration text mismatch in`, `Hydration text mismatch in`,
@ -180,6 +183,7 @@ export function createHydrationFunctions(
)}` + )}` +
`\n - expected on client: ${JSON.stringify(vnode.children)}`, `\n - expected on client: ${JSON.stringify(vnode.children)}`,
) )
logMismatchError()
;(node as Text).data = vnode.children as string ;(node as Text).data = vnode.children as string
} }
nextNode = nextSibling(node) nextNode = nextSibling(node)
@ -409,7 +413,6 @@ export function createHydrationFunctions(
) )
let hasWarned = false let hasWarned = false
while (next) { while (next) {
hasMismatch = true
if ( if (
(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && (__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
!hasWarned !hasWarned
@ -421,6 +424,8 @@ export function createHydrationFunctions(
) )
hasWarned = true hasWarned = true
} }
logMismatchError()
// The SSRed DOM contains more nodes than it should. Remove them. // The SSRed DOM contains more nodes than it should. Remove them.
const cur = next const cur = next
next = next.nextSibling next = next.nextSibling
@ -428,7 +433,6 @@ export function createHydrationFunctions(
} }
} else if (shapeFlag & ShapeFlags.TEXT_CHILDREN) { } else if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
if (el.textContent !== vnode.children) { if (el.textContent !== vnode.children) {
hasMismatch = true
;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
warn( warn(
`Hydration text content mismatch on`, `Hydration text content mismatch on`,
@ -436,6 +440,8 @@ export function createHydrationFunctions(
`\n - rendered on server: ${el.textContent}` + `\n - rendered on server: ${el.textContent}` +
`\n - expected on client: ${vnode.children as string}`, `\n - expected on client: ${vnode.children as string}`,
) )
logMismatchError()
el.textContent = vnode.children as string el.textContent = vnode.children as string
} }
} }
@ -455,7 +461,7 @@ export function createHydrationFunctions(
(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && (__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
propHasMismatch(el, key, props[key], vnode, parentComponent) propHasMismatch(el, key, props[key], vnode, parentComponent)
) { ) {
hasMismatch = true logMismatchError()
} }
if ( if (
(forcePatch && (forcePatch &&
@ -545,7 +551,6 @@ export function createHydrationFunctions(
// because server rendered HTML won't contain a text node // because server rendered HTML won't contain a text node
insert((vnode.el = createText('')), container) insert((vnode.el = createText('')), container)
} else { } else {
hasMismatch = true
if ( if (
(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && (__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
!hasWarned !hasWarned
@ -557,6 +562,8 @@ export function createHydrationFunctions(
) )
hasWarned = true hasWarned = true
} }
logMismatchError()
// the SSRed DOM didn't contain enough nodes. Mount the missing ones. // the SSRed DOM didn't contain enough nodes. Mount the missing ones.
patch( patch(
null, null,
@ -603,7 +610,8 @@ export function createHydrationFunctions(
} else { } else {
// fragment didn't hydrate successfully, since we didn't get a end anchor // fragment didn't hydrate successfully, since we didn't get a end anchor
// back. This should have led to node/children mismatch warnings. // back. This should have led to node/children mismatch warnings.
hasMismatch = true logMismatchError()
// since the anchor is missing, we need to create one and insert it // since the anchor is missing, we need to create one and insert it
insert((vnode.anchor = createComment(`]`)), container, next) insert((vnode.anchor = createComment(`]`)), container, next)
return next return next
@ -618,7 +626,6 @@ export function createHydrationFunctions(
slotScopeIds: string[] | null, slotScopeIds: string[] | null,
isFragment: boolean, isFragment: boolean,
): Node | null => { ): Node | null => {
hasMismatch = true
;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) &&
warn( warn(
`Hydration node mismatch:\n- rendered on server:`, `Hydration node mismatch:\n- rendered on server:`,
@ -631,6 +638,8 @@ export function createHydrationFunctions(
`\n- expected on client:`, `\n- expected on client:`,
vnode.type, vnode.type,
) )
logMismatchError()
vnode.el = null vnode.el = null
if (isFragment) { if (isFragment) {