Error Boundary 是个啥?

就是 React 版的"try-catch",专门用来抓渲染时的错误

先说结论:Error Boundary 是一个特殊的组件,它可以捕获子组件树里的错误,防止整个页面炸掉。

怎么实现?

必须是 Class 组件,用两个生命周期:

  1. static getDerivedStateFromError(error) — 渲染降级 UI
  2. componentDidCatch(error, errorInfo) — 记录错误日志
class ErrorBoundary extends React.Component {
  state = { hasError: false };

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

  componentDidCatch(error, errorInfo) {
    logErrorToMyService(error, errorInfo); // 上报监控
  }

  render() {
    if (this.state.hasError) {
      return <h1>出错了,请稍后再试</h1>;
    }
    return this.props.children;
  }
}

用法:

<ErrorBoundary>
  <MyComponent />
</ErrorBoundary>

能捕获哪些错误?

  • 渲染期间的错误
  • 生命周期里的错误
  • 构造函数里的错误
  • 子组件里的错误

不能捕获哪些错误?(重点)

1. 事件处理器里的错误

function MyButton() {
  const handleClick = () => {
    throw new Error("click error"); // 不会被捕获!
  };
  return <button onClick={handleClick}>点我</button>;
}

为什么不能捕获?

因为 React 的 Error Boundary 是在 渲染阶段 起作用。事件处理器里的代码是在事件触发后才执行的,这时候渲染已经完成了。

更重要的是:事件处理器的错误不会"破坏"渲染结果。点击一下报错了,但 React 依然可以把失败后的界面画出来。Error Boundary 的职责是"渲染出错了,页面保不住",而不是"某个交互失败了"。

正确做法:用 try/catch 包裹:

const handleClick = () => {
  try {
    // 可能出错的操作
  } catch (e) {
    console.error(e);
  }
};

2. 异步代码里的错误

function MyComponent() {
  useEffect(() => {
    setTimeout(() => {
      throw new Error("timeout error"); // 不会被捕获!
    }, 1000);
  }, []);
  return <div>组件</div>;
}

为什么不能捕获?

setTimeoutPromise.then()requestAnimationFrame 这些都是"在 React 渲染之外"执行的代码。它们由浏览器或 Node.js 调度,React 根本不知道它们的存在。

当这些异步任务抛出错误时,React 已经完成渲染了,没有任何人能"接住"这个错误。

正确做法:

// Promise 用 .catch
fetch("/api")
  .then()
  .catch((err) => console.error(err));

// setTimeout 在内部 try/catch
setTimeout(() => {
  try {
    // 可能出错
  } catch (e) {
    console.error(e);
  }
}, 1000);

// 或者用 unhandledrejection 事件监听
window.addEventListener("unhandledrejection", (e) => {
  console.error(e.reason);
});

3. Error Boundary 自身的错误

class ErrorBoundary extends React.Component {
  render() {
    throw new Error("边界自己炸了"); // 会被上层的边界捕获!
  }
}

<ErrorBoundary>
  {/* 这个边界自己错了,会冒泡到更外层的边界 */}
  <ErrorBoundary>
    <Child />
  </ErrorBoundary>
</ErrorBoundary>;

为什么不能自己捕获自己?

如果 Error Boundary 在 getDerivedStateFromErrorcomponentDidCatch 里也报错了,它自己是没办法处理的——因为它正在"处理错误"中,没法再处理"处理错误时的错误"。

这时候错误会继续向上冒泡,直到被上层的 Error Boundary 捕获。

4. 服务端渲染 (SSR)

// 服务端渲染时,Error Boundary 不生效
// 因为 SSR 没有"组件树"的概念,错误会直接导致整个服务崩溃

为什么不能捕获?

SSR 是在 Node.js 里"一次性"渲染 HTML,没有"渲染-错误-降级"这个过程。渲染过程中任何错误都会直接抛出,导致整个请求失败。

正确做法: SSR 时在服务端用 try/catch 包裹,或者用框架提供的错误边界(如 Next.js 的 error.tsx)。

总结

能捕获不能捕获为什么
渲染错误onClick 错误事件处理器在渲染后执行,错误不破坏渲染结果
生命周期错误异步代码错误setTimeout/Promise 在 React 渲染之外执行
子组件错误自身错误自身错误会冒泡到上层边界
SSRSSR 没有组件树,错误直接导致请求失败

记住:Error Boundary 只管渲染方面的错误,业务逻辑的错误还得靠 try/catch。