它是什么

包裹一段组件树,子树渲染抛错时显示 fallback,而不是整个应用白屏崩溃。

<ErrorBoundary fallback={<p>出错了,请刷新</p>}>
  <MaybeBuggy />
</ErrorBoundary>

现状:必须是 Class 组件

到 React 19 仍然如此。函数 Error Boundary 还在路上——目前用 class 或现成库(如 react-error-boundary)。

自己写

class ErrorBoundary extends React.Component<
  { fallback: React.ReactNode; children: React.ReactNode },
  { hasError: boolean }
> {
  state = { hasError: false };

  static getDerivedStateFromError(error: Error) {
    return { hasError: true };
  }

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    console.error('boundary caught:', error, info.componentStack);
    // 上报 Sentry / 自家 logger
  }

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

用 react-error-boundary 库(推荐)

npm i react-error-boundary
import { ErrorBoundary } from 'react-error-boundary';

function Fallback({ error, resetErrorBoundary }: any) {
  return (
    <div role="alert">
      <p>出错:{error.message}</p>
      <button onClick={resetErrorBoundary}>重试</button>
    </div>
  );
}

<ErrorBoundary
  FallbackComponent={Fallback}
  onError={(err, info) => reportToSentry(err, info)}
  onReset={() => /* 清理状态 */ null}
>
  <RealUI />
</ErrorBoundary>

捕获什么

  • 事件回调里的错误:要自己 try/catch
  • 异步代码里的错误:自己 .catch()
  • SSR 报错:那是服务器端的事(Next.js 有 error.tsx
  • Error Boundary 自己抛的错:要更外层一个 boundary 接住
function onClick() {
  try {
    doStuff();
  } catch (e) {
    console.error(e);   // boundary 不接这种
  }
}

在哪一层包

不是包整个应用一层就够——那样任何一处错误整页都崩

经验:

<RootBoundary>           // 兜底,整个应用一层
  <Layout>
    <RouteBoundary>      // 每个路由一层(Next.js 自动有 error.tsx)
      <Section>
        <WidgetBoundary> // 高风险小部件单独一层(如第三方图表)
          <ThirdPartyChart />
        </WidgetBoundary>
      </Section>
    </RouteBoundary>
  </Layout>
</RootBoundary>

Next.js 的内置错误处理

App Router 每个目录可以有 error.tsx

// 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>
  );
}

它是个自动 Error Boundary,包裹该路由段。

全局错误(包括布局自身报错)用 app/global-error.tsx

重置

错误 fallback 显示后,状态不会自动恢复——用户点重试需要把 boundary "reset"。

react-error-boundary 提供 resetErrorBoundary,并且当 resetKeys 数组里的值变了也会自动重置:

<ErrorBoundary FallbackComponent={Fallback} resetKeys={[userId]}>
  <UserPanel userId={userId} />
</ErrorBoundary>

切换 userId → boundary 自动重置(之前用户的错误不影响新用户)。

配 Suspense 用

<ErrorBoundary FallbackComponent={ErrorView}>
  <Suspense fallback={<Loading />}>
    <RemoteData />
  </Suspense>
</ErrorBoundary>

加载中走 Suspense;出错走 Error Boundary。这是 React 数据获取的现代范式。

→ 下一篇 受控 vs 非受控组件