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. 考虑错误边界的性能影响
相关推荐
ChoSeitaku2 小时前
No.1|Godot|俄罗斯方块复刻|棋盘和初始方块的设置
java·前端·godot
生信天地3 小时前
jQuery:前端开发的高效利器
前端·jquery
牛奶皮子3 小时前
vue3Class 与 Style 绑定
前端·javascript·vue.js
傻小胖3 小时前
React setState详细使用总结
前端·react.js·前端框架
网络安全Jack3 小时前
[CTF/网络安全] 攻防世界 Web_php_unserialize 解题详析
前端·web安全·php
宿命小人3 小时前
Electron使用记录
前端·javascript·electron
夫琅禾费米线4 小时前
react全局状态管理——redux和zustand,及其区别
前端·javascript·react.js
GISer_Jing4 小时前
React中createRoot函数原理解读——Element对象与Fiber对象、FiberRootNode与HostRootNode
前端·react.js·前端框架