improve: Refactor ErrorBoundary to use react-error-boundary library

This commit is contained in:
Haileyesus
2026-02-20 09:50:52 +03:00
parent 1e3eba930a
commit f9e2966977
3 changed files with 86 additions and 68 deletions

13
package-lock.json generated
View File

@@ -52,6 +52,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-error-boundary": "^4.1.2",
"react-i18next": "^16.5.3", "react-i18next": "^16.5.3",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"react-router-dom": "^6.8.1", "react-router-dom": "^6.8.1",
@@ -9834,6 +9835,18 @@
"react": ">= 16.8 || 18.0.0" "react": ">= 16.8 || 18.0.0"
} }
}, },
"node_modules/react-error-boundary": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.1.2.tgz",
"integrity": "sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5"
},
"peerDependencies": {
"react": ">=16.13.1"
}
},
"node_modules/react-i18next": { "node_modules/react-i18next": {
"version": "16.5.3", "version": "16.5.3",
"resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-16.5.3.tgz", "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-16.5.3.tgz",

View File

@@ -86,6 +86,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-error-boundary": "^4.1.2",
"react-i18next": "^16.5.3", "react-i18next": "^16.5.3",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"react-router-dom": "^6.8.1", "react-router-dom": "^6.8.1",

View File

@@ -1,73 +1,77 @@
import React from 'react'; import React, { useCallback, useState } from 'react';
import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';
class ErrorBoundary extends React.Component { function ErrorFallback({ error, resetErrorBoundary, showDetails, componentStack }) {
constructor(props) { return (
super(props); <div className="flex flex-col items-center justify-center p-8 text-center">
this.state = { hasError: false, error: null, errorInfo: null }; <div className="bg-red-50 border border-red-200 rounded-lg p-6 max-w-md">
} <div className="flex items-center mb-4">
<div className="flex-shrink-0">
static getDerivedStateFromError(error) { <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
// Update state so the next render will show the fallback UI <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
return { hasError: true }; </svg>
}
componentDidCatch(error, errorInfo) {
// Log the error details
console.error('ErrorBoundary caught an error:', error, errorInfo);
// You can also log the error to an error reporting service here
this.setState({
error: error,
errorInfo: errorInfo
});
}
render() {
if (this.state.hasError) {
// Fallback UI
return (
<div className="flex flex-col items-center justify-center p-8 text-center">
<div className="bg-red-50 border border-red-200 rounded-lg p-6 max-w-md">
<div className="flex items-center mb-4">
<div className="flex-shrink-0">
<svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
</svg>
</div>
<h3 className="ml-3 text-sm font-medium text-red-800">
Something went wrong
</h3>
</div>
<div className="text-sm text-red-700">
<p className="mb-2">An error occurred while loading the chat interface.</p>
{this.props.showDetails && this.state.error && (
<details className="mt-4">
<summary className="cursor-pointer text-xs font-mono">Error Details</summary>
<pre className="mt-2 text-xs bg-red-100 p-2 rounded overflow-auto max-h-40">
{this.state.error.toString()}
{this.state.errorInfo && this.state.errorInfo.componentStack}
</pre>
</details>
)}
</div>
<div className="mt-4">
<button
onClick={() => {
this.setState({ hasError: false, error: null, errorInfo: null });
if (this.props.onRetry) this.props.onRetry();
}}
className="bg-red-600 text-white px-4 py-2 rounded text-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500"
>
Try Again
</button>
</div>
</div> </div>
<h3 className="ml-3 text-sm font-medium text-red-800">
Something went wrong
</h3>
</div> </div>
); <div className="text-sm text-red-700">
} <p className="mb-2">An error occurred while loading the chat interface.</p>
{showDetails && error && (
<details className="mt-4">
<summary className="cursor-pointer text-xs font-mono">Error Details</summary>
<pre className="mt-2 text-xs bg-red-100 p-2 rounded overflow-auto max-h-40">
{error.toString()}
{componentStack}
</pre>
</details>
)}
</div>
<div className="mt-4">
<button
onClick={resetErrorBoundary}
className="bg-red-600 text-white px-4 py-2 rounded text-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500"
>
Try Again
</button>
</div>
</div>
</div>
);
}
return this.props.children; function ErrorBoundary({ children, showDetails = false, onRetry = undefined, resetKeys = undefined }) {
} const [componentStack, setComponentStack] = useState(null);
const handleError = useCallback((error, errorInfo) => {
console.error('ErrorBoundary caught an error:', error, errorInfo);
setComponentStack(errorInfo?.componentStack || null);
}, []);
const handleReset = useCallback(() => {
setComponentStack(null);
onRetry?.();
}, [onRetry]);
const renderFallback = useCallback(({ error, resetErrorBoundary }) => (
<ErrorFallback
error={error}
resetErrorBoundary={resetErrorBoundary}
showDetails={showDetails}
componentStack={componentStack}
/>
), [showDetails, componentStack]);
return (
<ReactErrorBoundary
fallbackRender={renderFallback}
onError={handleError}
onReset={handleReset}
resetKeys={resetKeys}
>
{children}
</ReactErrorBoundary>
);
} }
export default ErrorBoundary; export default ErrorBoundary;