1
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import * as React from "react";
|
||||
import { ErrorState } from "@/components/ui/error-state";
|
||||
|
||||
interface ErrorBoundaryState {
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
children: React.ReactNode;
|
||||
/** Render prop for full custom fallback */
|
||||
fallback?: (error: Error, reset: () => void) => React.ReactNode;
|
||||
/** Reset boundary when any value in this array changes (like `useEffect` deps) */
|
||||
resetKeys?: unknown[];
|
||||
/** Called once when a render error is caught */
|
||||
onError?: (error: Error, info: React.ErrorInfo) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Catches render-time errors anywhere below it and shows a fallback UI.
|
||||
* Wrap the whole app at the root (catches everything) and optionally
|
||||
* wrap individual routes/sections for finer-grained recovery.
|
||||
*
|
||||
* Note: does NOT catch errors inside event handlers, async code, or
|
||||
* server-side rendering. For those, surface errors via React Query
|
||||
* `onError` or `toast.error()`.
|
||||
*/
|
||||
export class ErrorBoundary extends React.Component<
|
||||
ErrorBoundaryProps,
|
||||
ErrorBoundaryState
|
||||
> {
|
||||
state: ErrorBoundaryState = { error: null };
|
||||
|
||||
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||
return { error };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: React.ErrorInfo) {
|
||||
this.props.onError?.(error, info);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("[ErrorBoundary]", error, info);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: ErrorBoundaryProps) {
|
||||
if (!this.state.error) return;
|
||||
const prev = prevProps.resetKeys ?? [];
|
||||
const next = this.props.resetKeys ?? [];
|
||||
if (
|
||||
prev.length !== next.length ||
|
||||
prev.some((v, i) => !Object.is(v, next[i]))
|
||||
) {
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
|
||||
reset = () => this.setState({ error: null });
|
||||
|
||||
render() {
|
||||
const { error } = this.state;
|
||||
if (!error) return this.props.children;
|
||||
if (this.props.fallback) return this.props.fallback(error, this.reset);
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-3xl px-6 py-16">
|
||||
<ErrorState
|
||||
severity="error"
|
||||
title="Something went wrong"
|
||||
description="An unexpected error broke this view. You can try reloading just this section, or refresh the page."
|
||||
detail={error.stack ?? error.message}
|
||||
onRetry={this.reset}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user