FE_Dev / components /error-boundary.tsx
GitHub Actions
Deploy from GitHub Actions [dev] - 2025-10-31 07:28:50
68f7925
'use client';
import { Button } from '@/components/ui/button';
import { AlertTriangle, ChevronLeft, RefreshCw } from 'lucide-react';
import React, { Component, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
showBackButton?: boolean;
backButtonText?: string;
backButtonAction?: () => void;
}
interface State {
hasError: boolean;
error?: Error;
}
export class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('ErrorBoundary caught an error:', error, errorInfo);
// シリアライゼーションエラーの詳細解析
if (error.message.includes('Only plain objects')) {
console.error('[SERIALIZATION_ERROR] シリアライゼーションエラーの詳細:');
console.error('[SERIALIZATION_ERROR] Error message:', error.message);
console.error('[SERIALIZATION_ERROR] Error stack:', error.stack);
console.error('[SERIALIZATION_ERROR] Component stack:', errorInfo.componentStack);
// エラーダイジェストが含まれている場合の解析
if ('digest' in error) {
console.error('[SERIALIZATION_ERROR] Error digest:', (error as Error & { digest?: string }).digest);
}
}
}
handleReset = () => {
this.setState({ hasError: false, error: undefined });
};
render() {
if (this.state.hasError) {
if (this.props.fallback) {
return <>{this.props.fallback}</>;
}
return (
<div className="flex min-h-[400px] flex-col items-center justify-center p-8">
<div className="mx-auto max-w-md text-center">
<AlertTriangle className="mx-auto mb-4 h-16 w-16 text-red-500" />
<h2 className="mb-2 text-2xl font-semibold text-gray-900">エラーが発生しました</h2>
<p className="mb-6 text-gray-600">
申し訳ございません。予期しないエラーが発生しました。
{this.state.error?.message && <span className="mt-2 block text-sm text-gray-500">{this.state.error.message}</span>}
</p>
<div className="flex justify-center gap-3">
{this.props.showBackButton && (
<Button
onClick={this.props.backButtonAction}
className="flex h-10 min-w-[120px] cursor-pointer items-center justify-center gap-2 rounded-full border-[1px] border-[#CCCCCC] bg-white px-6 text-[12px] font-semibold text-[#212121] shadow-none hover:bg-[#EEEEEE] hover:text-[#212121]"
>
<ChevronLeft className="h-4 w-4" />
{this.props.backButtonText || '戻る'}
</Button>
)}
<Button
onClick={this.handleReset}
className="flex h-10 min-w-[120px] cursor-pointer items-center justify-center gap-2 rounded-full bg-[#212121] px-6 text-[12px] font-semibold text-white shadow-none hover:bg-[#212121E5]"
>
<RefreshCw className="h-4 w-4" />
再試行
</Button>
</div>
</div>
</div>
);
}
return this.props.children;
}
}