跳到主要内容

error.js

error 文件允许你处理意外的运行时错误并显示回退 UI。

error.js 特殊文件error.js 特殊文件
app/dashboard/error.tsx
'use client' // 错误边界必须是客户端组件

import { useEffect } from 'react'

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
// 将错误记录到错误报告服务
console.error(error)
}, [error])

return (
<div>
<h2>出现了一些问题!</h2>
<button
onClick={
// 尝试通过重新渲染段来恢复
() => reset()
}
>
重试
</button>
</div>
)
}

error.js 将路由段及其嵌套子项包装在 React 错误边界中。当边界内抛出错误时,error 组件会显示为回退 UI。

error.js 工作原理error.js 工作原理

提示

  • React DevTools 允许你切换错误边界来测试错误状态。
  • 如果你希望错误冒泡到父错误边界,可以在渲染 error 组件时 throw

参考

Props

error

转发到 error.js 客户端组件的 Error 对象的实例。

提示:在开发过程中,转发到客户端的 Error 对象将被序列化并包含原始错误的 message,以便更容易调试。但是,此行为在生产环境中是不同的,以避免向客户端泄露错误中包含的潜在敏感详细信息。

error.message

  • 从客户端组件转发的错误显示原始 Error 消息。
  • 从服务端组件转发的错误显示带有标识符的通用消息。这是为了防止泄露敏感详细信息。你可以使用标识符(在 errors.digest 下)来匹配相应的服务端日志。

error.digest

自动生成的抛出错误的哈希值。可用于匹配服务端日志中的相应错误。

reset

错误的原因有时可能是临时的。在这些情况下,重试可能会解决问题。

错误组件可以使用 reset() 函数提示用户尝试从错误中恢复。执行时,该函数将尝试重新渲染错误边界的内容。如果成功,回退错误组件将被重新渲染的结果替换。

app/dashboard/error.tsx
'use client' // 错误边界必须是客户端组件

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div>
<h2>出现了一些问题!</h2>
<button onClick={() => reset()}>重试</button>
</div>
)
}

示例

全局错误

虽然不太常见,但你可以使用位于根应用目录中的 global-error.js 在根布局或模板中处理错误,即使在使用国际化时也是如此。全局错误 UI 必须定义自己的 <html><body> 标签。此文件在激活时替换根布局或模板。

app/global-error.tsx
'use client' // 错误边界必须是客户端组件

export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
// global-error 必须包含 html 和 body 标签
<html>
<body>
<h2>出现了一些问题!</h2>
<button onClick={() => reset()}>重试</button>
</body>
</html>
)
}

使用自定义错误边界的优雅错误恢复

当客户端渲染失败时,显示最后已知的服务端渲染 UI 可以提供更好的用户体验。

GracefullyDegradingErrorBoundary 是一个自定义错误边界的示例,它在错误发生前捕获并保留当前的 HTML。如果发生渲染错误,它会重新渲染捕获的 HTML 并显示一个持久的通知栏来通知用户。

app/dashboard/error.tsx
'use client'

import React, { Component, ErrorInfo, ReactNode } from 'react'

interface ErrorBoundaryProps {
children: ReactNode
onError?: (error: Error, errorInfo: ErrorInfo) => void
}

interface ErrorBoundaryState {
hasError: boolean
}

export class GracefullyDegradingErrorBoundary extends Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
private contentRef: React.RefObject<HTMLDivElement>

constructor(props: ErrorBoundaryProps) {
super(props)
this.state = { hasError: false }
this.contentRef = React.createRef()
}

static getDerivedStateFromError(_: Error): ErrorBoundaryState {
return { hasError: true }
}

componentDidCatch(error: Error, errorInfo: ErrorInfo) {
if (this.props.onError) {
this.props.onError(error, errorInfo)
}
}

render() {
if (this.state.hasError) {
// 渲染当前 HTML 内容而不进行水合
return (
<>
<div
ref={this.contentRef}
suppressHydrationWarning
dangerouslySetInnerHTML={{
__html: this.contentRef.current?.innerHTML || '',
}}
/>
<div className="fixed bottom-0 left-0 right-0 bg-red-600 text-white py-4 px-6 text-center">
<p className="font-semibold">
页面渲染期间发生错误
</p>
</div>
</>
)
}

return <div ref={this.contentRef}>{this.props.children}</div>
}
}

export default GracefullyDegradingErrorBoundary

版本历史

版本变更
v15.2.0在开发环境中也显示 global-error
v13.1.0引入 global-error
v13.0.0引入 error