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. 考虑错误边界的性能影响
相关推荐
晴空万里藏片云1 小时前
elment Table多级表头固定列后,合计行错位显示问题解决
前端·javascript·vue.js
曦月合一1 小时前
html中iframe标签 隐藏滚动条
前端·html·iframe
奶球不是球1 小时前
el-button按钮的loading状态设置
前端·javascript
kidding7231 小时前
前端VUE3的面试题
前端·typescript·compositionapi·fragment·teleport·suspense
Σίσυφος19003 小时前
halcon 条形码、二维码识别、opencv识别
前端·数据库
学代码的小前端3 小时前
0基础学前端-----CSS DAY13
前端·css
css趣多多4 小时前
案例自定义tabBar
前端
姑苏洛言6 小时前
DeepSeek写微信转盘小程序需求文档,这不比产品经理强?
前端
林的快手6 小时前
CSS列表属性
前端·javascript·css·ajax·firefox·html5·safari
匹马夕阳6 小时前
ECharts极简入门
前端·信息可视化·echarts