React Error Boundary 错误边界限制

1. 基本概念

1.1 什么是 Error Boundary

Error Boundary 是 React 16 引入的一个特性,它可以捕获子组件树中的 JavaScript 错误,记录错误并展示备用 UI,而不是让整个应用崩溃。大白话:嵌套组件某个组件出错时显示备用页面而非报错信息,影响整个页面显示

1.2 基本实现

jsx 复制代码
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 更新 state,下次渲染时显示降级 UI
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // 可以将错误日志上报给服务器
    console.error('Error:', error);
    console.error('Error Info:', errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 渲染降级 UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

2. 使用方式

2.1 基本用法

jsx 复制代码
// 包裹可能出错的组件
function App() {
  return (
    <ErrorBoundary>
      <MyComponent />
    </ErrorBoundary>
  );
}

// 嵌套使用
function ComplexApp() {
  return (
    <ErrorBoundary>
      <div>
        <ErrorBoundary>
          <ComponentA />
        </ErrorBoundary>
        <ErrorBoundary>
          <ComponentB />
        </ErrorBoundary>
      </div>
    </ErrorBoundary>
  );
}

2.2 自定义错误 UI

jsx 复制代码
class ErrorBoundary extends React.Component {
  state = { hasError: false, error: null };

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

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-container">
          <h2>Oops! Something went wrong</h2>
          <details>
            <summary>Error Details</summary>
            <pre>{this.state.error.toString()}</pre>
          </details>
          <button onClick={() => window.location.reload()}>
            Refresh Page
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

3. 高级用法

3.1 错误恢复

jsx 复制代码
class RecoverableErrorBoundary extends React.Component {
  state = { hasError: false };

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

  handleReset = () => {
    this.setState({ hasError: false });
  };

  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h2>Something went wrong</h2>
          <button onClick={this.handleReset}>
            Try Again
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

3.2 错误日志上报

jsx 复制代码
class LoggingErrorBoundary extends React.Component {
  componentDidCatch(error, errorInfo) {
    // 发送错误到日志服务
    logErrorToService(error, errorInfo);
  }

  async logErrorToService(error, errorInfo) {
    try {
      await fetch('/api/error-logging', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          error: error.toString(),
          componentStack: errorInfo.componentStack,
          timestamp: new Date().toISOString()
        })
      });
    } catch (loggingError) {
      console.error('Failed to log error:', loggingError);
    }
  }
}

3.3 条件性错误边界

jsx 复制代码
class ConditionalErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    // 只捕获特定类型的错误
    if (error instanceof CustomError) {
      return { hasError: true };
    }
    // 其他错误向上传播
    throw error;
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback || <div>Error occurred</div>;
    }

    return this.props.children;
  }
}

4. 实践模式

4.1 创建可重用的错误边界

jsx 复制代码
// 创建通用错误边界组件
class ReusableErrorBoundary extends React.Component {
  state = { hasError: false };

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

  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <div className="error-fallback">
          {this.props.errorMessage || 'Something went wrong'}
        </div>
      );
    }

    return this.props.children;
  }
}

// 使用示例
function App() {
  return (
    <ReusableErrorBoundary
      fallback={<CustomErrorUI />}
      errorMessage="Failed to load dashboard"
    >
      <Dashboard />
    </ReusableErrorBoundary>
  );
}

4.2 错误边界与 Suspense 结合

jsx 复制代码
function App() {
  return (
    <ErrorBoundary>
      <Suspense fallback={<Loading />}>
        <AsyncComponent />
      </Suspense>
    </ErrorBoundary>
  );
}

4.3 错误边界与 React Router 结合

jsx 复制代码
function App() {
  return (
    <Router>
      <ErrorBoundary>
        <Switch>
          <Route path="/dashboard">
            <ErrorBoundary>
              <Dashboard />
            </ErrorBoundary>
          </Route>
          <Route path="/profile">
            <ErrorBoundary>
              <Profile />
            </ErrorBoundary>
          </Route>
        </Switch>
      </ErrorBoundary>
    </Router>
  );
}

5. 最佳实践

5.1 错误边界的粒度

jsx 复制代码
function App() {
  return (
    <ErrorBoundary>
      {/* 顶层错误边界,捕获通用错误 */}
      <Layout>
        <ErrorBoundary>
          {/* 功能级错误边界 */}
          <UserDashboard />
        </ErrorBoundary>
        <ErrorBoundary>
          {/* 组件级错误边界 */}
          <ComplexChart />
        </ErrorBoundary>
      </Layout>
    </ErrorBoundary>
  );
}

5.2 错误恢复策略

jsx 复制代码
class SmartErrorBoundary extends React.Component {
  state = { hasError: false, retryCount: 0 };

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

  handleRetry = () => {
    this.setState(state => ({
      hasError: false,
      retryCount: state.retryCount + 1
    }));
  };

  render() {
    if (this.state.hasError) {
      if (this.state.retryCount >= 3) {
        return <div>Too many retries. Please refresh the page.</div>;
      }

      return (
        <div>
          <p>Something went wrong</p>
          <button onClick={this.handleRetry}>
            Retry (Attempt {this.state.retryCount + 1}/3)
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

6. 注意事项

6.1 错误边界的限制

  1. 不能捕获以下类型的错误:
    • 事件处理器中的错误
    • 异步代码中的错误
    • 服务端渲染中的错误
    • 错误边界自身抛出的错误

6.2 处理未捕获的错误

jsx 复制代码
// 在应用入口处添加全局错误处理
window.addEventListener('error', (event) => {
  console.error('Uncaught error:', event.error);
  // 显示全局错误 UI 或重新加载页面
});

window.addEventListener('unhandledrejection', (event) => {
  console.error('Unhandled promise rejection:', event.reason);
  // 处理未捕获的 Promise 错误
});

7. 总结

7.1 使用场景

  1. 数据获取错误处理
  2. 组件渲染错误处理
  3. 复杂计算错误处理
  4. 第三方组件错误隔离

7.2 最佳实践建议

  1. 合理设置错误边界的粒度
  2. 实现适当的错误恢复机制
  3. 做好错误日志记录
  4. 提供友好的用户错误提示
  5. 考虑错误边界的性能影响
相关推荐
半句唐诗15 分钟前
我是如何通过 Access Token 成功发布第一个 npm 包的
前端·npm·node.js
程序员黑豆19 分钟前
鸿蒙应用开发:@Provider 与 @Consumer 跨组件双向同步详解
前端·harmonyos
paopaokaka_luck24 分钟前
基于springboot3+vue3的智能文库平台(AI智能搜索、AI智能汇总、实时在线状态展示、多格式文档预览与富文本编辑、Echarts图形化分析)
前端·网络·spring boot·网络协议·echarts
牧艺36 分钟前
cos-design PhotoAlbum:用 CSS 3D 做一个「能翻页」的实体相册
前端·css·交互设计
洪贺41 分钟前
从原始采样到可缩放心电图:用 h5ECG 绘制自己的 ECG
前端
冰心孤城1 小时前
Excel: xls与xlsx格式转换排坑指南
java·前端·excel
程序员黑豆1 小时前
鸿蒙应用开发之跨组件传参:@Provide 与 @Consume 跨层级数据同步详解
前端·harmonyos
C++、Java和Python的菜鸟2 小时前
第9章 后端Web进阶(AOP)
java·开发语言·前端
我星期八休息3 小时前
扩展— TCP 全连接队列与 tcpdump 抓包
linux·服务器·开发语言·前端·网络·tcp/ip·tcpdump
AC赳赳老秦4 小时前
CSDN 技术社区数据采集:OpenClaw 抓取公开技术热帖,生成领域技术热点周报
java·大数据·前端·数据库·python·php·openclaw