From a5b9835b1ecf61b51557f5ba2b71d4196abd9e54 Mon Sep 17 00:00:00 2001 From: Dominik Horn Date: Thu, 27 Feb 2020 17:28:06 +0100 Subject: [PATCH 1/2] implement ErrorBoundary component #15 --- .../src/components/general/ErrorBoundary.tsx | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 packages/frontend/src/components/general/ErrorBoundary.tsx diff --git a/packages/frontend/src/components/general/ErrorBoundary.tsx b/packages/frontend/src/components/general/ErrorBoundary.tsx new file mode 100644 index 0000000..eee30d0 --- /dev/null +++ b/packages/frontend/src/components/general/ErrorBoundary.tsx @@ -0,0 +1,71 @@ +import * as React from 'react'; + +/** + * Properties of Error Boundary + */ +export interface ErrorBoundaryProps { + /** + * Callback used to render ui for the error once it occured + */ + readonly errorRenderer?: (error: Error) => React.ReactNode; +} + +interface State { + readonly error?: Error; +} + +function renderError(error: Error): React.ReactNode { + return error.message; +} + +/** + * ErrorBoundary component catches all errors that occur somewhere bellow in the DOM + * tree and displays an error message instead of taking the entire page down. + * + * Use by wrapping it around failable components in JSX + * or use the withErrorBoundary(...) to wrap component components + */ +export class ErrorBoundary extends React.Component { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = {}; + } + + static getDerivedStateFromError(error: Error) { + return { error }; + } + + componentDidCatch(error: Error, info: React.ErrorInfo) { + console.error('Caught error:', error, 'with info:', info); + console.trace(); + } + + render() { + const { error } = this.state; + if (error) { + return renderError(error); + } + return <>{this.props.children}; + } +} + +/** + * ErrorBoundary component catches all errors that occur somewhere bellow in the DOM + * tree and displays an error message instead of taking the entire page down. + * + * Use by wrapping it around failable components in JSX, i.e., ... + * or using this component wrapper + * + * @param WrappedComponent component to wrap + * @param errorRenderer callback to render error ui + */ +export function withErrorBoundary( + WrappedComponent: React.ComponentType, + errorRenderer: (error: Error) => React.ReactNode = renderError, +): React.ComponentType { + return (props: TProps) => ( + + + + ); +} From 011ca600a295a4a7d73a0cf41d988ef21912c6f4 Mon Sep 17 00:00:00 2001 From: Dominik Horn Date: Thu, 27 Feb 2020 17:28:57 +0100 Subject: [PATCH 2/2] wrap all top level routes in ErrorBoundary #15 --- packages/frontend/src/components/App.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/App.tsx b/packages/frontend/src/components/App.tsx index 67f2538..cea9bdc 100644 --- a/packages/frontend/src/components/App.tsx +++ b/packages/frontend/src/components/App.tsx @@ -7,6 +7,7 @@ import history from '../util/history'; import { FeatureFlagsProvider } from 'elite-feature-flags'; import { Configuration } from 'elite-types'; import { getConfiguration } from 'elite-configuration'; +import { ErrorBoundary } from './general/ErrorBoundary'; const configuration: Configuration = getConfiguration(); @@ -15,7 +16,9 @@ export const AppComponent = () => ( {APP_ROUTES.map((routeProps, index) => ( - + + + ))}