首页 / React 19 入门教程 / 错误边界

React 19 入门教程

错误边界

本教程共 50 篇 · 第 38 篇 · 更新于 2026-07-29 · 约 7 分钟阅读

React错误边界Error Boundary错误处理componentDidCatch调试

38. 错误边界

本节目标:学会用错误边界捕获组件渲染时的报错,防止整个应用白屏,给用户展示友好的错误提示。

没有错误边界的后果

一个组件渲染时抛出异常,如果没有被捕获,整个 React 树都会卸载。用户看到的就是白屏,没有任何提示。

function BuggyComponent() {
  throw new Error('我崩了');
  return <p>这行永远不会执行</p>;
}

function App() {
  return (
    <div>
      <h1>我的应用</h1>
      <BuggyComponent />  {/* 💥 整个 App 白屏 */}
    </div>
  );
}

错误边界就是来解决这个问题的——捕获子组件树的渲染错误,显示降级 UI,而不是崩溃整棵树

错误边界只能用类组件写

React 官方规定:错误边界必须用类组件。目前还没有”函数组件版”的错误边界 API。

import { Component } from 'react';

class ErrorBoundary extends Component {
  state = { hasError: false, error: null };

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

  componentDidCatch(error, errorInfo) {
    console.error('捕获到错误:', error, errorInfo);
    // 这里可以上报错误到监控服务
  }

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

两个关键生命周期方法:

  • getDerivedStateFromError:收到错误后更新 state,渲染降级 UI。必须是纯函数,不能有副作用。
  • componentDidCatch:在这里做日志上报、错误记录等副作用。

使用方式:

function App() {
  return (
    <ErrorBoundary fallback={<p>出错了,请刷新重试</p>}>
      <BuggyComponent />
    </ErrorBoundary>
  );
}
Note

错误边界只能捕获渲染期间的报错。事件处理、异步代码(setTimeout、fetch 回调)、服务端渲染的错误它捕获不到。

错误边界的捕获范围

错误边界能捕获的:

  • 子组件渲染时抛出的异常
  • 子组件生命周期方法中的报错(类组件)
  • 子组件构造函数中的报错

不能捕获的:

  • 事件处理函数里的报错(用 try/catch)
  • 异步代码里的报错
  • 服务端渲染的错误
  • 错误边界自身的报错
function App() {
  return (
    <ErrorBoundary fallback={<p>页面出错了</p>}>
      <Header />
      <ErrorBoundary fallback={<p>列表出错了</p>}>
        <List />
      </ErrorBoundary>
      <ErrorBoundary fallback={<p>侧边栏出错了</p>}>
        <Sidebar />
      </ErrorBoundary>
    </ErrorBoundary>
  );
}

可以嵌套使用,每个边界只管自己的子树。List 崩了不影响 Sidebar。

react-error-boundary:函数组件的救星

每次写类组件太烦了。社区有个 react-error-boundary 库,让你用函数组件的方式写错误边界:

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

function App() {
  return (
    <ErrorBoundary
      fallbackRender={({ error, resetErrorBoundary }) => (
        <div>
          <p>出错了:{error.message}</p>
          <button onClick={resetErrorBoundary}>重试</button>
        </div>
      )}
      onError={(error, info) => {
        // 上报错误
        reportError(error, info);
      }}
    >
      <BuggyComponent />
    </ErrorBoundary>
  );
}

fallbackRenderfallback 更灵活,能拿到错误对象和重置函数。

Tip

resetErrorBoundary 会重置错误状态,重新渲染子组件。用户点”重试”就能再试一次。

配合 Suspense 使用

React 19 的 use(Promise) 如果 Promise 被拒绝,错误会冒泡到最近的错误边界:

import { use, Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

function Message({ messagePromise }) {
  const content = use(messagePromise);
  return <p>{content}</p>;
}

function App() {
  return (
    <ErrorBoundary fallback={<p>加载失败</p>}>
      <Suspense fallback={<p>加载中……</p>}>
        <Message messagePromise={fetchMessage()} />
      </Suspense>
    </ErrorBoundary>
  );
}

Suspense 处理”加载中”,错误边界处理”加载失败”。各司其职。

错误边界的最佳实践

  1. 放在路由级别:每个路由页面包一个错误边界,一个页面崩了不影响其他页面
  2. 放在关键组件外:列表、卡片、侧边栏这些独立模块各自包一个
  3. 提供重试机制:用 resetErrorBoundary 让用户可以手动重试
  4. 上报错误:在 componentDidCatchonError 里把错误发到监控平台
// 路由级别的错误边界
function App() {
  return (
    <Routes>
      <Route path="/" element={
        <ErrorBoundary fallback={<HomeError />}>
          <HomePage />
        </ErrorBoundary>
      } />
      <Route path="/dashboard" element={
        <ErrorBoundary fallback={<DashboardError />}>
          <DashboardPage />
        </ErrorBoundary>
      } />
    </Routes>
  );
}
Warning

不要给每个小组件都包错误边界。错误边界是”最后防线”,不是常规错误处理。业务逻辑里的错误该 try/catch 就 try/catch。

事件处理里的错误

事件处理函数里的报错,错误边界捕获不到。你需要自己处理:

function DeleteButton() {
  const [error, setError] = useState(null);

  async function handleClick() {
    try {
      await deleteItem();
    } catch (err) {
      setError(err.message);
    }
  }

  if (error) return <p>删除失败:{error}</p>;

  return <button onClick={handleClick}>删除</button>;
}

或者用 state 配合错误边界:在事件处理里 setState 触发渲染错误,错误边界就能捕获了。但这不是推荐做法,还是 try/catch 更直接。