React 错误边界与 Suspense:优雅处理渲染异常与异步加载

test12026-09-122 次阅读

为什么需要错误边界

React 渲染期间抛出的错误如果不加处理,会从最近的边界一直冒泡到根组件,导致整棵树卸载、页面白屏。错误边界(Error Boundary)是 React 唯一能捕获渲染期异常的机制,它本质是一个实现了 getDerivedStateFromError 或 componentDidCatch 生命周期方法的类组件。

实现一个最小错误边界

注意:错误边界只能用类组件写,因为它依赖生命周期方法。函数组件没有等价 API,所以这是 React 里少数仍需类组件的场景。

import { Component, ReactNode } from 'react';

interface Props {
  fallback: ReactNode;
  children: ReactNode;
}
interface State {
  hasError: boolean;
}

export class ErrorBoundary extends Component<Props, State> {
  state: State = { hasError: false };

  static getDerivedStateFromError(): State {
    return { hasError: true };
  }

  componentDidCatch(error: Error) {
    console.error('render error:', error);
  }

  render() {
    if (this.state.hasError) return this.props.fallback;
    return this.props.children;
  }
}

配合 Suspense 处理异步加载

Suspense 负责在资源未就绪时展示 fallback,错误边界负责在资源或渲染失败时兜底。两者职责不同,要嵌套使用:外层 ErrorBoundary 捕获失败,内层 Suspense 展示加载中。

import { Suspense, lazy } from 'react';

const Chart = lazy(() => import('./Chart'));

function Page() {
  return (
    <ErrorBoundary fallback={<p>图表加载失败</p>}>
      <Suspense fallback={<p>加载中…</p>}>
        <Chart />
      </Suspense>
    </ErrorBoundary>
  );
}

让数据获取也能被 Suspense 捕获

React 18 的 Suspense 本身不获取数据,需要你提供一个在 pending 时 throw promise、在 rejected 时 throw error 的资源对象。下面是最简化的 suspense 资源封装:

function wrapPromise<T>(promise: Promise<T>) {
  let status = 'pending';
  let result: T;
  const suspender = promise.then(
    (r) => { status = 'success'; result = r; },
    (e) => { status = 'error'; result = e; }
  );
  return {
    read(): T {
      if (status === 'pending') throw suspender;
      if (status === 'error') throw result;
      return result;
    }
  };
}

三个容易踩的坑

  • 错误边界不能捕获事件处理函数里的异常,按钮 onClick 里的错误要用 try/catch 自己处理。
  • 一个错误边界只兜住它子树里的渲染错误,建议按功能模块粒度放置,而不是只在根上放一个。
  • 错误边界自身抛错会冒泡到更外层边界,生产环境务必配合监控上报,否则白屏无日志。
T

test1

文章作者

为什么需要错误边界 React 渲染期间抛出的错误如果不加处理,会从最近的边界一直冒泡到根组件,导致整棵树卸载、页面白...

分类
技术
发布时间
2026-09-12
字数
约 1901 字
阅读
2 次