video-flow-b/components/ui/error-boundary.tsx
2025-07-03 05:51:09 +08:00

90 lines
2.6 KiB
TypeScript

'use client';
import React from 'react';
import { AlertTriangle, RefreshCw } from 'lucide-react';
import { motion } from 'framer-motion';
interface ErrorBoundaryState {
hasError: boolean;
error: Error | null;
}
interface ErrorBoundaryProps {
children: React.ReactNode;
fallback?: React.ComponentType<{ error: Error; resetError: () => void }>;
}
export class ErrorBoundary extends React.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, errorInfo: React.ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
}
resetError = () => {
this.setState({ hasError: false, error: null });
};
render() {
if (this.state.hasError) {
if (this.props.fallback) {
const FallbackComponent = this.props.fallback;
return <FallbackComponent error={this.state.error!} resetError={this.resetError} />;
}
return (
<motion.div
className="flex flex-col items-center justify-center min-h-[400px] p-6 bg-red-500/10 border border-red-500/20 rounded-lg"
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3 }}
>
<AlertTriangle className="w-12 h-12 text-red-500 mb-4" />
<h3 className="text-lg font-semibold text-red-400 mb-2"></h3>
<p className="text-white/70 text-center mb-4 max-w-md">
{this.state.error?.message || '组件渲染时发生错误,请尝试刷新页面或重置组件'}
</p>
<motion.button
className="flex items-center gap-2 px-4 py-2 bg-red-500/20 text-red-400 border border-red-500/30 rounded-lg hover:bg-red-500/30 transition-colors"
onClick={this.resetError}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<RefreshCw className="w-4 h-4" />
</motion.button>
</motion.div>
);
}
return this.props.children;
}
}
// Hook 版本的错误边界
export function useErrorBoundary() {
const [error, setError] = React.useState<Error | null>(null);
const resetError = React.useCallback(() => {
setError(null);
}, []);
const captureError = React.useCallback((error: Error) => {
setError(error);
}, []);
React.useEffect(() => {
if (error) {
throw error;
}
}, [error]);
return { captureError, resetError };
}