Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
10 changes: 8 additions & 2 deletions packages/next/client/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -474,12 +474,18 @@ export function renderError(renderErrorProps: RenderErrorProps): Promise<any> {
return pageLoader
.loadPage('/_error')
.then(({ page: ErrorComponent, styleSheets }) => {
// To prevent infinite `_error` rendering when `_error` throws on client side
const Component =
lastAppProps?.Component === ErrorComponent
? () => <>An unexpected error has occurred</>
: ErrorComponent

// In production we do a normal render with the `ErrorComponent` as component.
// If we've gotten here upon initial render, we can use the props from the server.
// Otherwise, we need to call `getInitialProps` on `App` before mounting.
const AppTree = wrapApp(App)
const appCtx = {
Component: ErrorComponent,
Component,
AppTree,
router,
ctx: { err, pathname: page, query, asPath, AppTree },
Expand All @@ -492,7 +498,7 @@ export function renderError(renderErrorProps: RenderErrorProps): Promise<any> {
doRender({
...renderErrorProps,
err,
Component: ErrorComponent,
Component,
styleSheets,
props: initProps,
})
Expand Down
2 changes: 1 addition & 1 deletion test/integration/build-output/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe('Build Output', () => {
expect(parseFloat(err404FirstLoad)).toBeCloseTo(67.1, 0)
expect(err404FirstLoad.endsWith('kB')).toBe(true)

expect(parseFloat(sharedByAll)).toBeCloseTo(63.8, 1)
expect(parseFloat(sharedByAll)).toBeCloseTo(63.9, 1)
expect(sharedByAll.endsWith('kB')).toBe(true)

if (_appSize.endsWith('kB')) {
Expand Down
23 changes: 23 additions & 0 deletions test/integration/error-in-error-client/pages/_error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react'

class Error extends React.Component {
static async getInitialProps({ res, err }) {
const statusCode = res ? res.statusCode : err ? err.statusCode : null
return { statusCode }
}

render() {
if (typeof window !== 'undefined') {
throw new Error('bar')
}
return (
<p>
{this.props.statusCode
? `An error ${this.props.statusCode} occurred on server`
: 'An error occurred on client'}
</p>
)
}
}

export default Error
21 changes: 21 additions & 0 deletions test/integration/error-in-error-client/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Link from 'next/link'

function Index() {
return (
<>
<h3>Hi 👋</h3>
<Link href="/a-non-existing-page">
<a>a link to no-where</a>
</Link>
</>
)
}

Index.getInitialProps = () => {
if (typeof window !== 'undefined') {
throw new Error('foo')
}
return {}
}

export default Index
33 changes: 33 additions & 0 deletions test/integration/error-in-error-client/test/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-env jest */

import path from 'path'
import webdriver from 'next-webdriver'
import {
nextBuild,
nextStart,
findPort,
killApp,
waitFor,
} from 'next-test-utils'

jest.setTimeout(1000 * 60 * 1)
const appDir = path.join(__dirname, '..')
let app
let port

describe('Handles an client Error in _error', () => {
beforeAll(async () => {
await nextBuild(appDir)
port = await findPort()
app = await nextStart(appDir, port)
})
afterAll(() => killApp(app))

it('Handles error during client transition', async () => {
const browser = await webdriver(port, '/')
await browser.waitForElementByCss('a').click()
await waitFor(1000)
const html = await browser.eval('document.body.innerHTML')
expect(html).toMatch(/An unexpected error has occurred/i)
})
})