React 错误处理与稳定性:Error Boundary、日志与降级策略

业务代码写多了,就会遇到这样的问题:某个组件里一个异常没处理好,把整棵应用直接打挂。
和其说是在写新功能,不如说是在为「出问题时还能撑住场面」做准备。

Error Boundary 只能在 class 组件里实现,但可以用来保护一整颗子树:

 1type ErrorBoundaryProps = {
 2  fallback: React.ReactNode;
 3  onError?: (error: Error, info: React.ErrorInfo) => void;
 4};
 5
 6type ErrorBoundaryState = {
 7  hasError: boolean;
 8};
 9
10class ErrorBoundary extends React.Component<
11  ErrorBoundaryProps,
12  ErrorBoundaryState
13> {
14  constructor(props: ErrorBoundaryProps) {
15    super(props);
16    this.state = { hasError: false };
17  }
18
19  static getDerivedStateFromError() {
20    return { hasError: true };
21  }
22
23  componentDidCatch(error: Error, info: React.ErrorInfo) {
24    this.props.onError?.(error, info);
25  }
26
27  render() {
28    if (this.state.hasError) {
29      return this.props.fallback;
30    }
31    return this.props.children;
32  }
33}

用法很直接:

1<ErrorBoundary fallback={<div>出错了,请稍后重试</div>}>
2  <MainApp />
3</ErrorBoundary>

或者只包住某些「风险较高」的区域,比如富文本编辑器、可视化画布等。

能捕获的:

  • 渲染过程中抛出的异常;
  • 生命周期方法里的异常;
  • 由这些组件触发的事件处理函数里的同步异常。

不能捕获的:

  • 异步回调里的异常(setTimeout、Promise.then 等);
  • 服务端渲染(SSR)阶段抛出的错误;
  • 自身 Error Boundary 组件内部抛出的错误。

因此,对异步请求、手动抛错的业务逻辑,仍然需要自己做 try/catch 或在数据层做统一处理。

错误本身只是现象,更关键的是在生产环境里能及时感知和定位。
Error Boundary 的 onError 钩子可以用来接入日志系统:

 1function logError(error: Error, info: React.ErrorInfo) {
 2  // 发送到日志系统,例如 Sentry、自研监控等
 3}
 4
 5<ErrorBoundary
 6  fallback={<ErrorPage />}
 7  onError={logError}
 8>
 9  <MainApp />
10</ErrorBoundary>

合适的策略通常包括:

  • 给错误打上应用版本、用户信息(脱敏后)和路由信息;
  • 区分前端渲染错误与后端接口错误;
  • 对重复的错误进行聚合,避免被单一错误刷屏。

错误不可避免,重点是「挂了之后用户还能不能做事」。
常见的降级分几层:

  • 局部降级:某个 widget 出错,只替换这块区域为简化版本或提示信息;
  • 页面级降级:某个页面关键逻辑出错,引导回到上一页或首页;
  • 全局降级:全局 Error Boundary 捕获到异常时,展示一个简单但可用的 fallback(例如仅导航 + 反馈入口)。

举个局部降级的例子:

1<ErrorBoundary fallback={<div>图表加载失败</div>}>
2  <Chart />
3</ErrorBoundary>

Chart 挂了,页面其他部分照样可用。

大部分真实场景里的错误,来自于异步请求或业务规则检查。
这类错误更适合在数据层做处理,而不是试图让 Error Boundary 去兜:

  • 对于请求错误:在数据 fetching 层(如 React Query 的 onError)统一收集和提示;
  • 对于业务错误:约定好错误结构和展示方式(toast、inline 提示等),前端只负责按结构展示。

这样一来:

  • Error Boundary 主要负责意料之外的渲染崩溃;
  • 数据层负责可预期的失败分支。
  • Error Boundary 是 React 里处理渲染阶段异常的最后一道防线,适合按区域包裹子树,配合统一的错误日志上报。
  • 它不能替代异步错误处理,setTimeout、Promise 等回调里的异常需要在业务逻辑或数据层自己兜住。
  • 合理设计局部/页面/全局三层降级策略,可以让应用在局部出问题时仍保持可用。
  • 把「意料之外的错误」交给 Error Boundary,「可预期的失败分支」交给数据层和业务逻辑,各司其职更容易维护。