import { Component, type ReactNode, type ErrorInfo } from 'react'
import { Icon } from '@/components/common/Icon'
interface ErrorBoundaryProps {
children: ReactNode
fallback?: ReactNode
onError?: (error: Error, info: ErrorInfo) => void
}
interface ErrorBoundaryState {
hasError: Boolean
error: Error | null
}
/**
* 通用错误边界。捕获子组件渲染错误,防止整页白屏。
*/
export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props)
this.state = { hasError: false, error: null }
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error }
}
componentDidCatch(error: Error, info: ErrorInfo): void {
console.error('[ErrorBoundary] 捕获到渲染错误:', error, info.componentStack)
this.props.onError?.(error, info)
}
handleRetry = () => {
this.setState({ hasError: false, error: null })
}
render() {
if (this.state.hasError) {
if (this.props.fallback) return this.props.fallback
return (
<div className="flex flex-col items-center justify-center gap-3 p-8 text-center">
<Icon name="error_outline" size="xl" className="text-[var(--color-text-tertiary)]" />
<div>
<p className="text-sm font-medium text-[var(--color-text-secondary)]">页面加载失败</p>
<p className="text-xs text-[var(--color-text-tertiary)] mt-1">
{this.state.error?.message ?? '未知错误'}
</p>
</div>
<button
onClick={this.handleRetry}
className="px-4 py-1.5 text-xs font-medium rounded-lg bg-[color:var(--color-brand-500)] text-white hover:bg-[color:var(--color-brand-600)] transition-colors"
>
重试
</button>
</div>
)
}
return this.props.children
}
}
|