import { Component } from 'react'; import { View, Text } from '@tarojs/components'; import './index.scss'; interface Props { children: React.ReactNode; fallback?: React.ReactNode; } interface State { hasError: boolean; retryCount: number; errorCategory: ErrorCategory; } type ErrorCategory = 'network' | 'render' | 'unknown'; const MAX_RETRIES = 3; function classifyError(error: Error): ErrorCategory { const msg = error.message?.toLowerCase() || ''; if ( msg.includes('network') || msg.includes('fetch') || msg.includes('timeout') || msg.includes('request:fail') ) { return 'network'; } if ( msg.includes('cannot read properties') || msg.includes('is not defined') || msg.includes('is not a function') || msg.includes('render') ) { return 'render'; } return 'unknown'; } function logError(error: Error, info: React.ErrorInfo, category: ErrorCategory): void { const isDev = process.env.NODE_ENV === 'development'; const entry = { ts: new Date().toISOString(), category, message: error.message, stack: isDev ? error.stack : undefined, componentStack: isDev ? info.componentStack : undefined, }; if (isDev) { console.error('[ErrorBoundary]', JSON.stringify(entry, null, 2)); } else { console.error('[ErrorBoundary]', entry.ts, entry.category, entry.message); } } export default class ErrorBoundary extends Component { constructor(props: Props) { super(props); this.state = { hasError: false, retryCount: 0, errorCategory: 'unknown' }; } static getDerivedStateFromError(error: Error): Partial { return { hasError: true, errorCategory: classifyError(error) }; } componentDidCatch(error: Error, info: React.ErrorInfo) { const category = classifyError(error); logError(error, info, category); this.setState((prev) => ({ retryCount: prev.retryCount + 1, errorCategory: category, })); } handleRetry = () => { this.setState({ hasError: false }); }; render() { if (this.state.hasError) { if (this.props.fallback) { return this.props.fallback; } const exceeded = this.state.retryCount >= MAX_RETRIES; const isNetwork = this.state.errorCategory === 'network'; const title = isNetwork ? '网络连接失败' : '页面出了点问题'; const desc = exceeded ? '请重启小程序后重试' : isNetwork ? '请检查网络后重试' : '请返回重试'; return ( ! {title} {desc} {!exceeded && ( 重新加载 )} ); } return this.props.children; } }