11'use client'
22
3- import React , { type JSX } from 'react'
3+ import React , { startTransition , type JSX } from 'react'
44import { useUntrackedPathname } from './navigation-untracked'
55import { isNextRouterError } from './is-next-router-error'
66import { handleHardNavError } from './nav-failure-handler'
77import { HandleISRError } from './handle-isr-error'
88import { isBot } from '../../shared/lib/router/utils/is-bot'
9+ import { publicAppRouterInstance } from './app-router-instance'
10+ import {
11+ AppRouterContext ,
12+ type AppRouterInstance ,
13+ } from '../../shared/lib/app-router-context.shared-runtime'
914
1015const isBotUserAgent =
1116 typeof window !== 'undefined' && isBot ( window . navigator . userAgent )
@@ -15,6 +20,9 @@ export type ErrorComponent = React.ComponentType<{
1520 // global-error, there's no `reset` function;
1621 // regular error boundary, there's a `reset` function.
1722 reset ?: ( ) => void
23+ retry ?: ( ) => void
24+ componentStack ?: string
25+ ownerStack ?: string
1826} >
1927
2028export interface ErrorBoundaryProps {
@@ -32,17 +40,30 @@ interface ErrorBoundaryHandlerProps extends ErrorBoundaryProps {
3240interface ErrorBoundaryHandlerState {
3341 error : Error | null
3442 previousPathname : string | null
43+ componentStack ?: string
44+ ownerStack ?: string
3545}
3646
3747export class ErrorBoundaryHandler extends React . Component <
3848 ErrorBoundaryHandlerProps ,
3949 ErrorBoundaryHandlerState
4050> {
51+ static contextType = AppRouterContext
52+ declare context : AppRouterInstance | null
53+
4154 constructor ( props : ErrorBoundaryHandlerProps ) {
4255 super ( props )
4356 this . state = { error : null , previousPathname : this . props . pathname }
4457 }
4558
59+ componentDidCatch ( _error : Error , info : React . ErrorInfo ) {
60+ this . setState ( {
61+ componentStack : info . componentStack || undefined ,
62+ // @ts -expect-error ownerStack is not yet in @types/react
63+ ownerStack : info . ownerStack ,
64+ } )
65+ }
66+
4667 static getDerivedStateFromError ( error : Error ) {
4768 if ( isNextRouterError ( error ) ) {
4869 // Re-throw if an expected internal Next.js router error occurs
@@ -95,6 +116,14 @@ export class ErrorBoundaryHandler extends React.Component<
95116 this . setState ( { error : null } )
96117 }
97118
119+ retry = ( ) => {
120+ startTransition ( ( ) => {
121+ publicAppRouterInstance . refresh ( )
122+ this . context ?. refresh ( )
123+ this . reset ( )
124+ } )
125+ }
126+
98127 // Explicit type is needed to avoid the generated `.d.ts` having a wide return type that could be specific to the `@types/react` version.
99128 render ( ) : React . ReactNode {
100129 //When it's bot request, segment level error boundary will keep rendering the children,
@@ -108,6 +137,9 @@ export class ErrorBoundaryHandler extends React.Component<
108137 < this . props . errorComponent
109138 error = { this . state . error }
110139 reset = { this . reset }
140+ retry = { this . retry }
141+ componentStack = { this . state . componentStack }
142+ ownerStack = { this . state . ownerStack }
111143 />
112144 </ >
113145 )
0 commit comments