/** * React Component for a simple Error Boundary. The fallback UI for * this error boundary is simply a bordered text box */ import * as React from "react"; import { EventEmitter } from "../../utils/EventEmitter"; type IProps = { eventEmitterForReset?: EventEmitter; id?: string; }; type IState = { errorInfo: string; hasError: boolean; } type IErrorInfo = { componentStack: string; } // TODO: Move this out to a css file const styleMarkup = { border: "1px solid red", display: "inline-block", margin: "4px", padding: "4px", } export class ErrorBoundary extends React.Component { constructor(props: IProps) { super(props); this.state = { errorInfo: "", hasError: false, } } componentDidCatch(error: Error, info: IErrorInfo): void { console.error(`Caught error in React ErrorBoundary. Component stack:`); console.error(info.componentStack); } componentDidMount(): void { const cb = (): void => { this.setState({ hasError: false, }); } if (this.hasEventEmitter()) { (this.props.eventEmitterForReset as EventEmitter).addSubscriber({ cb: cb, id: (this.props.id as string), }); } } componentWillUnmount(): void { if (this.hasEventEmitter()) { (this.props.eventEmitterForReset as EventEmitter).removeSubscriber((this.props.id as string)); } } hasEventEmitter(): boolean { return this.props.eventEmitterForReset != null && this.props.eventEmitterForReset instanceof EventEmitter && this.props.id != null && typeof this.props.id === "string"; } render(): React.ReactNode { if (this.state.hasError) { return (

{ `Error rendering UI. This is (probably) a bug. Please report to game developer.` }

{ `In the meantime, try refreshing the game WITHOUT saving.` }

{ `Error info: ${this.state.errorInfo}` }

) } return this.props.children; } static getDerivedStateFromError(error: Error): IState { return { errorInfo: error.message, hasError: true, }; } }