错误边界是什么?React 应用的错误怎么处理?

进阶高频实践约 8 分钟读完

一句话回答

错误边界是一个能捕获子组件树渲染过程中抛出的错误、并显示降级 UI 的组件,避免一个组件出错导致整个应用白屏(没有被捕获的渲染错误会让 React 卸载整棵树)。目前只能用类组件实现:static getDerivedStateFromError 返回新 state 来显示降级 UI,componentDidCatch 做上报;函数组件项目一般直接用 react-error-boundary。它捕获不到事件处理函数、异步代码(setTimeout、Promise 回调)、服务端渲染和错误边界自身的错误,这些要用 try/catch 处理,或者手动转交给错误边界。React 19 还可以在 createRoot 上配置 onCaughtError、onUncaughtError 统一上报。

详细解析

为什么需要错误边界

React 16 起,渲染过程中抛出的错误如果没有被任何错误边界捕获,React 会卸载整个组件树,页面直接空白。因为渲染到一半的界面可能是错的,显示错误的数据(比如付款金额)比白屏更糟。错误边界让你决定"哪一块坏了,就只让哪一块显示出错提示"。

两个生命周期

getDerivedStateFromError(error) componentDidCatch(error, info)
调用阶段 render 阶段 commit 阶段
作用 返回新 state,切换到降级 UI 执行副作用,比如上报日志
要求 必须是纯函数 可以有副作用

info.componentStack 是出错组件到根组件的组件栈,生产环境下组件名被压缩了,需要配合 Source Map 还原。目前没有对应的 Hook,所以错误边界只能写成类组件。

捕获不到的错误

  • 事件处理函数:点击回调里的错误不发生在渲染过程中,React 不需要靠它决定界面怎么显示,用 try/catch 自己处理
  • 异步代码:setTimeout、requestAnimationFrame、Promise 回调执行时,渲染早已结束。例外:React 19 中 useTransition 的 startTransition 里抛出的错误(包括 async 函数)会交给错误边界
  • 服务端渲染:Suspense 边界内的组件在服务端出错时,React 会在 HTML 里输出这个边界的 fallback,再到客户端重试渲染
  • 错误边界自身:它自己 render 出错时,会交给更上层的错误边界

事件和异步中的错误想用错误边界统一展示,可以 catch 后存进 state,在下次渲染时 throw;react-error-boundary 提供的 useErrorBoundary 封装了这件事。

放在哪里

  • 根部一个:兜底,显示"页面出错了,请刷新"
  • 路由级别:一个页面出错不影响导航栏和其他页面
  • 独立模块:侧边栏、评论区、第三方组件、图表这类可以单独坏掉的区域各包一个

边界不是越细越好:每个按钮都包一个,界面会出现大量零散的错误提示,用户反而不知道发生了什么。按"用户能理解的功能块"来划分。

上报和监控

React 19 的 createRoot 和 hydrateRoot 在选项中支持三个回调,参数都是 (error, errorInfo):onCaughtError 在错误被错误边界捕获时调用,onUncaughtError 在没有被捕获时调用,onRecoverableError 在 React 自动恢复的错误(如 hydrateRoot 遇到水合不匹配,改为在客户端重新渲染)时调用。在这里统一上报,比在每个错误边界里分别上报更不容易漏。全局错误监听、白屏检测等前端监控方案见 前端监控。

代码示例

JSX
class ErrorBoundary extends Component {
  state = { error: null }

  // render 阶段调用:返回新的 state,下次渲染显示降级 UI,必须是纯函数
  static getDerivedStateFromError(error) {
    return { error }
  }

  // commit 阶段调用:适合做上报这类副作用
  componentDidCatch(error, info) {
    reportError({ message: error.message, componentStack: info.componentStack })
  }

  reset = () => this.setState({ error: null })

  render() {
    if (this.state.error) {
      return this.props.fallback({ error: this.state.error, reset: this.reset })
    }
    return this.props.children
  }
}

// 使用
<ErrorBoundary fallback={({ error, reset }) => (
  <div>
    <p>出错了:{error.message}</p>
    <button onClick={reset}>重试</button>
  </div>
)}>
  <Profile user={user} />
</ErrorBoundary>

用 react-error-boundary 处理事件中的异步错误:

JSX
import { ErrorBoundary, useErrorBoundary } from 'react-error-boundary'

function LoadButton() {
  const { showBoundary } = useErrorBoundary()
  async function handleClick() {
    try {
      await loadData()
    } catch (err) {
      showBoundary(err) // 交给最近的错误边界显示
    }
  }
  return <button onClick={handleClick}>加载</button>
}

// <ErrorBoundary fallbackRender={({ error, resetErrorBoundary }) => ...} resetKeys={[userId]}>

resetKeys 中的值变化时会自动重置边界,比如切换到另一个用户时重新尝试渲染。

面试官可能追问

为什么错误边界不捕获事件处理函数里的错误?

错误边界的职责是"渲染失败时决定显示什么"。事件处理函数里的错误不会让当前界面处于渲染了一半的状态,界面本身仍然是完整的,React 把它当作普通的 JS 异常,由开发者用 try/catch 决定提示方式。需要显示降级 UI 时,再通过 state 或 showBoundary 转交给错误边界。

重试之后马上又报同样的错,怎么办?

重置边界只是清掉错误状态、重新渲染子组件,如果导致出错的数据没变,会立刻再次出错。重试时要同时改变数据:重新请求、清空出错的缓存,或者用 resetKeys、给边界设置 key,在依赖的数据变化时才重置。

被错误边界捕获的错误,window.onerror 能收到吗?

React 19 中收不到。React 18 及以前,开发环境会把渲染错误重新抛出,即使被错误边界捕获,window 的 error 事件也会触发,控制台还会重复打印。React 19 不再重新抛出:被捕获的错误只用 console.error 打印,未被捕获的错误交给 window.reportError。依赖全局错误监听做上报的项目,升级后要改用 onCaughtError、onUncaughtError 或 componentDidCatch。

易错点

  • 以为错误边界能捕获所有错误:事件处理函数、setTimeout、普通 Promise 回调中的错误都捕获不到
  • 在 getDerivedStateFromError 里做上报,它在 render 阶段执行,可能被调用多次,上报应该放在 componentDidCatch
  • 整个应用只在根部放一个错误边界,任何小模块出错都会让整页变成错误页

AI 模拟面试官

用自己的话回答,AI 对照参考答案打分、指出遗漏,再追问,最多 3 轮

登录后就可以和 AI 面试官对练,面试记录也会保存下来。登录

这道题你掌握了吗?

选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。

学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。