/** * @file React Error Boundary for catching unhandled render-time errors. * @copyright nhcarrigan * @license Naomi's Public License * @author Naomi Carrigan */ import { Component, type ErrorInfo, type ReactNode } from "react"; import { logError } from "../utils/logError.js"; interface ErrorBoundaryProperties { readonly children: ReactNode; } interface ErrorBoundaryState { hasError: boolean; } /** * Catches unhandled render-time errors in the React tree, logs them to the * backend telemetry service, and renders a fallback UI. */ class ErrorBoundary extends Component< ErrorBoundaryProperties, ErrorBoundaryState > { // eslint-disable-next-line jsdoc/require-jsdoc -- React Error Boundary constructor is standard boilerplate public constructor(properties: ErrorBoundaryProperties) { super(properties); this.state = { hasError: false }; } /** * Updates state so the next render shows the fallback UI. * @returns The updated error boundary state. */ public static getDerivedStateFromError(): ErrorBoundaryState { return { hasError: true }; } /** * Logs the error to the backend telemetry service. * @param error - The error that was thrown during render. * @param info - React error info containing the component stack trace. */ // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- React lifecycle method cannot be static public override componentDidCatch(error: Error, info: ErrorInfo): void { logError("react_error_boundary", error, info.componentStack); } /** * Renders the fallback UI when an error is caught, otherwise renders children. * @returns The JSX element. */ public override render(): ReactNode { const { hasError } = this.state; const { children } = this.props; if (hasError) { return (
{"Something went wrong. Please refresh the page."}