错误边界处理

3 阅读2分钟

引言

在 React 应用中,一个组件的错误不应导致整个应用崩溃。错误边界(Error Boundaries)是 React 提供的错误隔离机制,它能捕获子组件树中的 JavaScript 错误,并显示降级 UI 而非让整个应用白屏。


什么是错误边界?

错误边界是类组件,通过实现 getDerivedStateFromError()componentDidCatch() 生命周期方法来捕获子组件的错误。

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

  static getDerivedStateFromError(error) {
    // 渲染降级 UI
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // 记录错误日志
    console.error('捕获错误:', error, errorInfo);
    this.setState({ error });
  }

  render() {
    if (this.state.hasError) {
      return <FallbackUI error={this.state.error} />;
    }
    return this.props.children;
  }
}

核心 API 对比

方法调用时机用途能否访问组件实例
getDerivedStateFromError渲染阶段更新 state 显示降级 UI❌ 静态方法
componentDidCatch提交阶段记录错误日志、上报✅ 可访问实例

实战:完整的错误边界组件

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

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

  componentDidCatch(error, errorInfo) {
    this.setState({ error, errorInfo });
    
    // 上报到错误监控平台
    logErrorToService(error, errorInfo);
  }

  handleRetry = () => {
    this.setState({ hasError: false, error: null, errorInfo: null });
  };

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-boundary">
          <h2>😕 出错了</h2>
          <p>组件渲染失败,请尝试刷新</p>
          <button onClick={this.handleRetry}>重试</button>
          {process.env.NODE_ENV === 'development' && (
            <details>
              <summary>错误详情</summary>
              <pre>{this.state.error?.toString()}</pre>
            </details>
          )}
        </div>
      );
    }
    return this.props.children;
  }
}

错误上报策略

// 错误上报服务
async function logErrorToService(error, errorInfo) {
  const errorData = {
    message: error.message,
    stack: error.stack,
    componentStack: errorInfo.componentStack,
    url: window.location.href,
    userAgent: navigator.userAgent,
    timestamp: Date.now()
  };

  // 使用 sendBeacon 确保上报成功
  navigator.sendBeacon('/api/log-error', JSON.stringify(errorData));
  
  // 或发送到第三方监控平台
  // Sentry.captureException(error, { contexts: { react: errorInfo } });
}

使用场景

// ✅ 包裹可能出错的子组件
<ErrorBoundary>
  <UserProfile userId={123} />
</ErrorBoundary>

// ✅ 多个独立边界,隔离错误
<ErrorBoundary fallback={<ChatFallback />}>
  <ChatWidget />
</ErrorBoundary>

<ErrorBoundary fallback={<FeedFallback />}>
  <NewsFeed />
</ErrorBoundary>

// ❌ 不要包裹整个应用(失去隔离意义)
<ErrorBoundary>
  <App />
</ErrorBoundary>

注意事项

能捕获不能捕获
子组件渲染错误事件处理器中的错误
生命周期错误异步代码(setTimeout、requestAnimationFrame)
构造函数错误SSR 服务端错误
边界组件自身的错误

总结

  1. 错误边界必须是类组件(Hooks 方案需用第三方库如 react-error-boundary)
  2. 精细化包裹:在可能出错的组件周围单独设置边界
  3. 优雅降级:提供友好的错误提示和重试机制
  4. 错误上报:记录错误信息用于后续分析修复
  5. 开发环境:显示详细错误信息便于调试