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. 考虑错误边界的性能影响
相关推荐
webYin25 分钟前
vue2 打包生成的js文件过大优化
前端·vue.js·webpack
gnip27 分钟前
结合Worker通知应用更新
前端·javascript
叶玳言1 小时前
【LVGL】从HTML到LVGL:嵌入式UI的设计迁移与落地实践
前端·ui·html·移植
高级测试工程师欧阳1 小时前
HTML 基本结构
前端
Gazer_S1 小时前
【Element Plus 表单组件样式统一 & CSS 文字特效实现指南】
前端·css·vue.js
一只小阿乐1 小时前
Html重绘和重排
前端·html
_Rookie._1 小时前
vue3 使用css变量
前端·javascript·html
杨超越luckly1 小时前
HTML应用指南:利用GET请求获取全国招商银行网点位置信息
前端·arcgis·信息可视化·html·银行网点
蔗理苦1 小时前
2025-09-04 HTML3——区块布局与表单
前端·css·html
GIS之路1 小时前
GDAL 开发起步
前端