Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add error link when hydration error occurs #31519

Merged
merged 7 commits into from
Nov 23, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions errors/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"title": "Messages",
"heading": true,
"routes": [
{
"title": "react-hydration-error",
"path": "/errors/react-hydration-error.md"
},
{
"title": "beta-middleware",
"path": "/errors/beta-middleware.md"
Expand Down
17 changes: 17 additions & 0 deletions errors/react-hydration-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# React Hydration Error

#### Why This Error Occurred
timneutkens marked this conversation as resolved.
Show resolved Hide resolved

While rendering your application, different content was returned on the server than on the first render on the client (hydration).

This can cause the react tree to be out of sync with the DOM and result in unexpected content/attributes being present.

#### Possible Ways to Fix It
ijjk marked this conversation as resolved.
Show resolved Hide resolved

Look for any differences in rendering on the server that rely on `typeof window` or `process.browser` checks that could cause a difference when rendered in the browser and delay these until after the component has mounted (after hydration).

Ensure consistent values are being used, for example, don't render `Date.now()` directly in the component tree and instead set an initial value in `getStaticProps` which is updated after hydration `useEffect(() => {}, [])`.

### Useful Links
ijjk marked this conversation as resolved.
Show resolved Hide resolved

- [React Hydration Documentation](https://reactjs.org/docs/react-dom.html#hydrate)
19 changes: 19 additions & 0 deletions packages/next/client/next-dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@ const webpackHMR = initWebpackHMR()

connectHMR({ assetPrefix: prefix, path: '/_next/webpack-hmr' })

if (!window._nextSetupHydrationWarning) {
const origConsoleError = window.console.error
window.console.error = (...args) => {
const isHydrateError = args.some(
(arg) =>
typeof arg === 'string' &&
arg.match(/Warning:.*?did not match.*?Server:/)
)
if (isHydrateError) {
args = [
...args,
`\n\nSee more info here: https://nextjs.org/docs/messages/react-hydration-error`,
]
}
origConsoleError.apply(window.console, args)
}
window._nextSetupHydrationWarning = true
}

window.next = {
version,
// router is initialized later so it has to be live-binded
Expand Down
2 changes: 1 addition & 1 deletion packages/react-dev-overlay/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ function onBuildError(message: string) {
}

function onRefresh() {
Bus.emit({ type: Bus.TYPE_REFFRESH })
Bus.emit({ type: Bus.TYPE_REFRESH })
}

export { getNodeError } from './internal/helpers/nodeStackFrames'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function reducer(state: OverlayState, ev: Bus.BusEvent): OverlayState {
case Bus.TYPE_BUILD_ERROR: {
return { ...state, buildError: ev.message }
}
case Bus.TYPE_REFFRESH: {
case Bus.TYPE_REFRESH: {
return { ...state, buildError: null, errors: [] }
}
case Bus.TYPE_UNHANDLED_ERROR:
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dev-overlay/src/internal/bus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { StackFrame } from 'stacktrace-parser'

export const TYPE_BUILD_OK = 'build-ok'
export const TYPE_BUILD_ERROR = 'build-error'
export const TYPE_REFFRESH = 'fast-refresh'
export const TYPE_REFRESH = 'fast-refresh'
export const TYPE_UNHANDLED_ERROR = 'unhandled-error'
export const TYPE_UNHANDLED_REJECTION = 'unhandled-rejection'

Expand All @@ -11,7 +11,7 @@ export type BuildError = {
type: typeof TYPE_BUILD_ERROR
message: string
}
export type FastRefresh = { type: typeof TYPE_REFFRESH }
export type FastRefresh = { type: typeof TYPE_REFRESH }
export type UnhandledError = {
type: typeof TYPE_UNHANDLED_ERROR
reason: Error
Expand Down
5 changes: 5 additions & 0 deletions test/integration/auto-export/pages/[post]/[cmnt].js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ if (typeof window !== 'undefined') {
export default function Page() {
if (typeof window !== 'undefined') {
window.pathnames.push(window.location.pathname)

if (window.location.pathname.includes('hydrate-error')) {
return <p>hydration error</p>
}
}
// eslint-disable-next-line
return <p>{useRouter().asPath}</p>
}
12 changes: 12 additions & 0 deletions test/integration/auto-export/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,17 @@ describe('Auto Export', () => {
const caughtWarns = await browser.eval(`window.caughtWarns`)
expect(caughtWarns).toEqual([])
})

it('should include error link when hydration error does occur', async () => {
const browser = await webdriver(appPort, '/post-1/hydrate-error')
const logs = await browser.log()
expect(
logs.some((log) =>
log.message.includes(
'See more info here: https://nextjs.org/docs/messages/react-hydration-error'
)
)
).toBe(true)
})
})
})