diff options
Diffstat (limited to 'src/components/react/ErrorBoundary.tsx')
| -rw-r--r-- | src/components/react/ErrorBoundary.tsx | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/src/components/react/ErrorBoundary.tsx b/src/components/react/ErrorBoundary.tsx new file mode 100644 index 0000000..4945094 --- /dev/null +++ b/src/components/react/ErrorBoundary.tsx @@ -0,0 +1,63 @@ +import React, { Component } from 'react' +import type { ErrorInfo, ReactNode } from 'react' + +interface Props { + children: ReactNode + fallback?: ReactNode +} + +interface State { + hasError: boolean + error: Error | null +} + +export class ErrorBoundary extends Component<Props, State> { + constructor(props: Props) { + super(props) + this.state = { hasError: false, error: null } + } + + static getDerivedStateFromError(error: Error): State { + return { hasError: true, error } + } + + componentDidCatch(error: Error, errorInfo: ErrorInfo) { + // Log error to console in development + if (import.meta.env.DEV) { + console.error('ErrorBoundary caught an error:', error, errorInfo) + } + // In production, you could send to error tracking service + // Example: Sentry.captureException(error, { contexts: { react: errorInfo } }) + } + + render() { + if (this.state.hasError) { + if (this.props.fallback) { + return this.props.fallback + } + + return ( + <div + className="rounded-lg border border-destructive/50 bg-destructive/10 p-6" + role="alert" + aria-live="assertive" + > + <h2 className="text-lg font-semibold text-destructive mb-2"> + Something went wrong + </h2> + <p className="text-sm text-muted-foreground"> + {this.state.error?.message || 'An unexpected error occurred'} + </p> + <button + onClick={() => this.setState({ hasError: false, error: null })} + className="mt-4 text-sm text-primary hover:underline" + > + Try again + </button> + </div> + ) + } + + return this.props.children + } +} |
