-
Notifications
You must be signed in to change notification settings - Fork 27.7k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
re-add hydration support to React 18 errors #69757
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,15 +23,42 @@ const htmlTagsWarnings = new Set([ | |
"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.", | ||
]) | ||
|
||
export const getHydrationWarningType = (msg: NullableText): 'tag' | 'text' => { | ||
// In React 18, the warning message is prefixed with "Warning: " | ||
const normalizeWarningMessage = (msg: string) => msg.replace(/^Warning: /, '') | ||
|
||
// Note: React 18 only | ||
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', | ||
]) | ||
|
||
// Note: React 18 only | ||
const textMismatchWarning = | ||
'Warning: Text content did not match. Server: "%s" Client: "%s"%s' | ||
|
||
const isTextMismatchWarning = (msg: NullableText) => textMismatchWarning === msg | ||
const isTextInTagsMismatchWarning = (msg: NullableText) => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
Boolean(msg && textAndTagsMismatchWarnings.has(msg)) | ||
|
||
export const getHydrationWarningType = ( | ||
msg: NullableText | ||
): 'tag' | 'text' | 'text-in-tag' => { | ||
if (isHtmlTagsWarning(msg)) return 'tag' | ||
return 'text' | ||
} | ||
|
||
const isHtmlTagsWarning = (msg: NullableText) => | ||
Boolean(msg && htmlTagsWarnings.has(msg)) | ||
const isHtmlTagsWarning = (msg: NullableText) => { | ||
if (msg && typeof msg === 'string') { | ||
return htmlTagsWarnings.has(normalizeWarningMessage(msg)) | ||
} | ||
|
||
return false | ||
} | ||
|
||
const isKnownHydrationWarning = (msg: NullableText) => isHtmlTagsWarning(msg) | ||
const isKnownHydrationWarning = (msg: NullableText) => | ||
isHtmlTagsWarning(msg) || | ||
isTextInTagsMismatchWarning(msg) || | ||
isTextMismatchWarning(msg) | ||
|
||
export const getReactHydrationDiffSegments = (msg: NullableText) => { | ||
if (msg) { | ||
|
@@ -51,14 +78,18 @@ export const getReactHydrationDiffSegments = (msg: NullableText) => { | |
export function storeHydrationErrorStateFromConsoleArgs(...args: any[]) { | ||
const [msg, serverContent, clientContent, componentStack] = args | ||
if (isKnownHydrationWarning(msg)) { | ||
hydrationErrorState.warning = [ | ||
// remove the last %s from the message | ||
msg, | ||
serverContent, | ||
clientContent, | ||
] | ||
hydrationErrorState.warning = [msg, serverContent, clientContent] | ||
hydrationErrorState.componentStack = componentStack | ||
hydrationErrorState.serverContent = serverContent | ||
hydrationErrorState.clientContent = clientContent | ||
|
||
return [ | ||
...args, | ||
// We tack on the hydration error message to the console.error message so that | ||
// it matches the error we display in the redbox overlay | ||
`\nSee more info here: https://nextjs.org/docs/messages/react-hydration-error`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Supports behavior that was lost from: |
||
] | ||
} | ||
|
||
return args | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,8 @@ import { FileRef, nextTestSetup } from 'e2e-utils' | |
import { outdent } from 'outdent' | ||
import path from 'path' | ||
|
||
describe('Error overlay for hydration errors (React 19)', () => { | ||
// TODO: Enable once React 19 support is added to pages. | ||
describe.skip('Error overlay for hydration errors (React 19)', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can't run because we throw in pages if 18 is used |
||
const { next } = nextTestSetup({ | ||
files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), | ||
skipStart: true, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,7 @@ import { FileRef, nextTestSetup } from 'e2e-utils' | |
import { outdent } from 'outdent' | ||
import path from 'path' | ||
|
||
// TODO: Enable this test once react 18 is supported for pages router | ||
describe.skip('Error overlay for hydration errors (React 18)', () => { | ||
describe('Error overlay for hydration errors (React 18)', () => { | ||
const { next } = nextTestSetup({ | ||
files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), | ||
dependencies: { | ||
|
@@ -38,7 +37,7 @@ describe.skip('Error overlay for hydration errors (React 18)', () => { | |
await session.assertHasRedbox() | ||
|
||
expect(await session.getRedboxDescription()).toMatchInlineSnapshot(` | ||
"Error: Text content does not match server-rendered HTML. | ||
"Text content does not match server-rendered HTML. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slight change in behavior but showing |
||
See more info here: https://nextjs.org/docs/messages/react-hydration-error" | ||
`) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverts the
Errors.tsx
changes from:to match the React 18 behavior when not in app dir