Skip to content

Commit

Permalink
Handle hydration errors in React 18
Browse files Browse the repository at this point in the history
This reverts commit 6dfa1c5.
  • Loading branch information
eps1lon committed Sep 24, 2024
1 parent 9cfcd6a commit c5ceb4f
Show file tree
Hide file tree
Showing 3 changed files with 690 additions and 282 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function PseudoHtmlDiff({
firstContent: string
secondContent: string
reactOutputComponentDiff: string | undefined
hydrationMismatchType: 'tag' | 'text'
hydrationMismatchType: 'tag' | 'text' | 'text-in-tag'
} & React.HTMLAttributes<HTMLPreElement>) {
const isHtmlTagsWarning = hydrationMismatchType === 'tag'
const isReactHydrationDiff = !!reactOutputComponentDiff
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,62 @@ export const hydrationErrorState: HydrationErrorState = {}

// https://github.com/facebook/react/blob/main/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js used as a reference
const htmlTagsWarnings = new Set([
'In HTML, %s cannot be a child of <%s>.%s\nThis will cause a hydration error.%s',
'In HTML, %s cannot be a descendant of <%s>.\nThis will cause a hydration error.%s',
'In HTML, text nodes cannot be a child of <%s>.\nThis will cause a hydration error.',
"In HTML, whitespace text nodes cannot be a child of <%s>. Make sure you don't have any extra whitespace between tags on each line of your source code.\nThis will cause a hydration error.",
'Warning: In HTML, %s cannot be a child of <%s>.%s\nThis will cause a hydration error.%s',
'Warning: In HTML, %s cannot be a descendant of <%s>.\nThis will cause a hydration error.%s',
'Warning: In HTML, text nodes cannot be a child of <%s>.\nThis will cause a hydration error.',
"Warning: In HTML, whitespace text nodes cannot be a child of <%s>. Make sure you don't have any extra whitespace between tags on each line of your source code.\nThis will cause a hydration error.",
'Warning: Expected server HTML to contain a matching <%s> in <%s>.%s',
'Warning: Did not expect server HTML to contain a <%s> in <%s>.%s',
])
const textAndTagsMismatchWarnings = new Set([
'Warning: Expected server HTML to contain a matching text node for "%s" in <%s>.%s',
'Warning: Did not expect server HTML to contain the text node "%s" in <%s>.%s',
])
const textMismatchWarning =
'Warning: Text content did not match. Server: "%s" Client: "%s"%s'

export const getHydrationWarningType = (
message: NullableText
): 'tag' | 'text' | 'text-in-tag' => {
if (typeof message !== 'string') {
// TODO: Doesn't make sense to treat no message as a hydration error message.
// We should bail out somewhere earlier.
return 'text'
}

const normalizedMessage = message.startsWith('Warning: ')
? message
: `Warning: ${message}`

if (isHtmlTagsWarning(normalizedMessage)) return 'tag'
if (isTextInTagsMismatchWarning(normalizedMessage)) return 'text-in-tag'

export const getHydrationWarningType = (msg: NullableText): 'tag' | 'text' => {
if (isHtmlTagsWarning(msg)) return 'tag'
return 'text'
}

const isHtmlTagsWarning = (msg: NullableText) =>
Boolean(msg && htmlTagsWarnings.has(msg))
const isHtmlTagsWarning = (message: string) => htmlTagsWarnings.has(message)

const isKnownHydrationWarning = (msg: NullableText) => isHtmlTagsWarning(msg)
const isTextMismatchWarning = (message: string) =>
textMismatchWarning === message
const isTextInTagsMismatchWarning = (msg: string) =>
textAndTagsMismatchWarnings.has(msg)

const isKnownHydrationWarning = (message: NullableText) => {
if (typeof message !== 'string') {
return false
}
// React 18 has the `Warning: ` prefix.
// React 19 does not.
const normalizedMessage = message.startsWith('Warning: ')
? message
: `Warning: ${message}`

return (
isHtmlTagsWarning(normalizedMessage) ||
isTextInTagsMismatchWarning(normalizedMessage) ||
isTextMismatchWarning(normalizedMessage)
)
}

export const getReactHydrationDiffSegments = (msg: NullableText) => {
if (msg) {
Expand Down
Loading

0 comments on commit c5ceb4f

Please sign in to comment.