字节笔记本

2026年2月22日

手把手教你实现 react-error-boundary

本文介绍一个手把手教你实现 react-error-boundary 的开源项目。该项目通过实战教程,详细讲解如何从零开始构建一个功能完善的 React 错误边界组件,帮助开发者更好地处理 React 应用中的异常错误。

项目简介

my-react-error-bounday 是一个由 haixiangyan 开发的开源教学项目,旨在通过造轮子的方式帮助开发者深入理解 React Error Boundary 的工作原理。截至目前,该项目在 GitHub 上已获得 65 stars,主要使用 TypeScript (86.6%) 编写。

项目通过生动有趣的故事形式,从实际开发中遇到的问题出发,逐步引导开发者实现一个功能完整的 Error Boundary 组件。

核心特性

  • 完整的类型支持:使用 TypeScript 编写,提供完整的类型定义
  • 多种 fallback 展示方式:支持 fallback 元素、FallbackComponent 组件、fallbackRender 函数三种方式
  • 手动重置功能:提供 onReset 和 resetErrorBoundary 方法实现手动重置
  • 自动重置机制:通过 resetKeys 监听数组变化自动重置错误状态
  • 高阶组件支持:提供 withErrorBoundary 高阶函数简化组件包裹
  • 自定义错误处理:提供 useErrorHandler Hook 让开发者自主抛出错误

技术栈

  • React:核心 UI 框架
  • TypeScript:类型安全的 JavaScript 超集
  • React Hooks:使用 useState 等 Hooks 实现状态管理

核心概念

Error Boundary 是什么

Error Boundary 是 React 提供的一种错误处理机制,用于捕获子组件树中的 JavaScript 错误,记录这些错误,并显示备用 UI 而不是让组件树崩溃。

关键生命周期方法

  • getDerivedStateFromError:当错误发生时更新 state,使下一次渲染显示降级 UI
  • componentDidCatch:捕获错误信息,可用于错误日志上报

实现步骤

第一步:基础 ErrorBoundary

从 React 官网示例开始,创建一个基础的 ErrorBoundary 类组件:

typescript
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

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

  componentDidCatch(error, errorInfo) {
    logger.error(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

第二步:灵活的 Fallback 配置

扩展组件以支持多种 fallback 传入方式:

typescript
interface FallbackProps {
  error: Error;
}

interface ErrorBoundaryProps {
  fallback?: React.ReactElement;
  FallbackComponent?: React.ComponentType<FallbackProps>;
  fallbackRender?: (props: FallbackProps) => React.ReactElement;
  onError?: (error: Error, info: string) => void;
}

// 在 render 中根据优先级选择 fallback 展示方式
if (error !== null) {
  if (React.isValidElement(fallback)) {
    return fallback;
  }
  if (typeof fallbackRender === 'function') {
    return fallbackRender({ error });
  }
  if (FallbackComponent) {
    return <FallbackComponent error={error} />;
  }
}

第三步:添加重置功能

实现手动重置和自动重置机制:

typescript
interface ErrorBoundaryProps {
  // ... 其他 props
  onReset?: () => void;
  resetKeys?: Array<string | number>;
  onResetKeysChange?: (prevKeys: Array<string | number>,
                       newKeys: Array<string | number>) => void;
}

resetErrorBoundary = () => {
  if (this.props.onReset) {
    this.props.onReset();
  }
  this.setState({ error: null });
}

第四步:高阶组件封装

提供 withErrorBoundary 简化组件包裹:

typescript
function withErrorBoundary<P>(
  Component: React.ComponentType<P>,
  errorBoundaryProps: ErrorBoundaryProps
): React.ComponentType<P> {
  const Wrapped: React.ComponentType<P> = props => {
    return (
      <ErrorBoundary {...errorBoundaryProps}>
        <Component {...props}/>
      </ErrorBoundary>
    )
  }

  const name = Component.displayName || Component.name || 'Unknown';
  Wrapped.displayName = `withErrorBoundary(${name})`;
  return Wrapped;
}

第五步:自定义错误抛出

提供 useErrorHandler Hook 处理非自动捕获的错误:

typescript
function useErrorHandler<P = Error>(
  givenError?: P | null | undefined,
): React.Dispatch<React.SetStateAction<P | null>> {
  const [error, setError] = React.useState<P | null>(null);

  if (givenError) throw givenError;
  if (error) throw error;

  return setError;
}

使用示例

基础用法

tsx
<ErrorBoundary fallback={<div>出错了</div>}>
  <UserList />
</ErrorBoundary>

使用 FallbackComponent

tsx
const ErrorFallback = ({ error }: { error: Error }) => (
  <div>
    <h2>出错了</h2>
    <p>{error.message}</p>
  </div>
);

<ErrorBoundary FallbackComponent={ErrorFallback}>
  <UserList />
</ErrorBoundary>

使用高阶组件

tsx
const UserWithErrorBoundary = withErrorBoundary(User, {
  onError: (error) => console.error('出错:', error),
  onReset: () => console.log('已重置')
});

// 在父组件中直接使用
const App = () => <UserWithErrorBoundary />;

配合自定义错误处理

tsx
function Greeting() {
  const [name, setName] = React.useState('');
  const { greeting, error } = useGreeting(name);

  // 将错误抛给 ErrorBoundary 处理
  useErrorHandler(error);

  return greeting ? <div>{greeting}</div> : <Form />;
}

export default withErrorBoundary(Greeting);

最佳实践

  1. 合理放置 ErrorBoundary:在关键业务组件外层包裹 ErrorBoundary,避免整个应用崩溃
  2. 提供友好的错误提示:fallback UI 应该清晰告知用户发生了什么,并提供操作建议
  3. 错误上报:在 onError 回调中将错误信息上报到监控系统
  4. 重置策略:根据业务场景选择手动重置或自动重置

项目链接

总结

my-react-error-bounday 项目通过一个生动有趣的故事,完整地演示了如何从零实现一个生产级的 React Error Boundary 组件。项目不仅实现了基础功能,还提供了多种灵活的使用方式,是学习 React 错误处理机制的绝佳教程。

分享: