diff options
Diffstat (limited to 'src/components/ErrorBoundary.tsx')
-rw-r--r-- | src/components/ErrorBoundary.tsx | 128 |
1 files changed, 68 insertions, 60 deletions
diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index ed565c5..aa1e889 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -16,6 +16,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +import { LazyComponent } from "../utils"; import Logger from "../utils/logger"; import { Margins, React } from "../webpack/common"; import { ErrorCard } from "./ErrorCard"; @@ -32,68 +33,75 @@ const logger = new Logger("React ErrorBoundary", color); const NO_ERROR = {}; -export default class ErrorBoundary extends React.Component<React.PropsWithChildren<Props>> { - static wrap<T = any>(Component: React.ComponentType<T>): (props: T) => React.ReactElement { - return props => ( - <ErrorBoundary> - <Component {...props as any/* I hate react typings ??? */} /> - </ErrorBoundary> - ); - } - - state = { - error: NO_ERROR as any, - stack: "", - message: "" - }; +// We might want to import this in a place where React isn't ready yet. +// Thus, wrap in a LazyComponent +const ErrorBoundary = LazyComponent(() => { + return class ErrorBoundary extends React.PureComponent<React.PropsWithChildren<Props>> { + state = { + error: NO_ERROR as any, + stack: "", + message: "" + }; - static getDerivedStateFromError(error: any) { - let stack = error?.stack ?? ""; - let message = error?.message || String(error); + static getDerivedStateFromError(error: any) { + let stack = error?.stack ?? ""; + let message = error?.message || String(error); - if (error instanceof Error && stack) { - const eolIdx = stack.indexOf("\n"); - if (eolIdx !== -1) { - message = stack.slice(0, eolIdx); - stack = stack.slice(eolIdx + 1).replace(/https:\/\/\S+\/assets\//g, ""); + if (error instanceof Error && stack) { + const eolIdx = stack.indexOf("\n"); + if (eolIdx !== -1) { + message = stack.slice(0, eolIdx); + stack = stack.slice(eolIdx + 1).replace(/https:\/\/\S+\/assets\//g, ""); + } } + + return { error, stack, message }; } - return { error, stack, message }; - } - - componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { - this.props.onError?.(error, errorInfo); - logger.error("A component threw an Error\n", error); - logger.error("Component Stack", errorInfo.componentStack); - } - - render() { - if (this.state.error === NO_ERROR) return this.props.children; - - if (this.props.fallback) - return <this.props.fallback - children={this.props.children} - {...this.state} - />; - - const msg = this.props.message || "An error occurred while rendering this Component. More info can be found below and in your console."; - - return ( - <ErrorCard style={{ - overflow: "hidden", - }}> - <h1>Oh no!</h1> - <p>{msg}</p> - <code> - {this.state.message} - {!!this.state.stack && ( - <pre className={Margins.marginTop8}> - {this.state.stack} - </pre> - )} - </code> - </ErrorCard> - ); - } -} + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + this.props.onError?.(error, errorInfo); + logger.error("A component threw an Error\n", error); + logger.error("Component Stack", errorInfo.componentStack); + } + + render() { + if (this.state.error === NO_ERROR) return this.props.children; + + if (this.props.fallback) + return <this.props.fallback + children={this.props.children} + {...this.state} + />; + + const msg = this.props.message || "An error occurred while rendering this Component. More info can be found below and in your console."; + + return ( + <ErrorCard style={{ + overflow: "hidden", + }}> + <h1>Oh no!</h1> + <p>{msg}</p> + <code> + {this.state.message} + {!!this.state.stack && ( + <pre className={Margins.marginTop8}> + {this.state.stack} + </pre> + )} + </code> + </ErrorCard> + ); + } + }; +}) as + React.ComponentType<React.PropsWithChildren<Props>> & { + wrap<T extends JSX.IntrinsicAttributes = any>(Component: React.ComponentType<T>): React.ComponentType<T>; + }; + +ErrorBoundary.wrap = Component => props => ( + <ErrorBoundary> + <Component {...props} /> + </ErrorBoundary> +); + +export default ErrorBoundary; |