"use client"; import { Component, FunctionComponent, ReactNode } from "react"; import { Context as RawRoutingContext } from "../../apps/client/contexts/routing-context"; type Props = { children?: ReactNode; path: string; taggedErrorComponents: { tag: string; component: FunctionComponent<{ error: unknown; reset: () => void; }>; }[]; }; type State = { error: (Error & { digest: string }) | null; }; export default class CatchBoundary extends Component { static contextType = RawRoutingContext; declare context: React.ContextType; #errorVersion: number | null = null; constructor(props: Props) { super(props); this.state = { error: null, }; } static getDerivedStateFromError(error: unknown) { if (error instanceof Error) { return { error, }; } throw error; } reset() { this.#errorVersion = null; this.setState({ error: null, }); } componentDidCatch() { this.#errorVersion = this.context.version; } componentDidUpdate() { if ( this.state.error && this.#errorVersion && this.context.version !== this.#errorVersion ) { this.#errorVersion = null; this.setState({ error: null, }); } } static #digestToTag: Record = { TwofoldUnauthorizedError: "unauthorized", TwofoldNotFoundError: "not-found", }; render() { if (this.state.error) { let digest = this.state.error.digest; let errorTag = CatchBoundary.#digestToTag[digest] ?? "unexpected"; const taggedComponent = this.props.taggedErrorComponents.find( (tagged) => tagged.tag === errorTag, ); if (!taggedComponent) { throw this.state.error; } return ( ); } else { return this.props.children; } } }