2022-10-11 11:17:10 +02:00
|
|
|
import React from 'react'
|
|
|
|
|
|
|
|
export type ErrorComponent = React.ComponentType<{
|
|
|
|
error: Error
|
|
|
|
reset: () => void
|
|
|
|
}>
|
|
|
|
interface ErrorBoundaryProps {
|
|
|
|
errorComponent: ErrorComponent
|
2022-11-08 14:49:13 +01:00
|
|
|
errorStyles?: React.ReactNode | undefined
|
2022-10-11 11:17:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles errors through `getDerivedStateFromError`.
|
|
|
|
* Renders the provided error component and provides a way to `reset` the error boundary state.
|
|
|
|
*/
|
|
|
|
class ErrorBoundaryHandler extends React.Component<
|
|
|
|
ErrorBoundaryProps,
|
|
|
|
{ error: Error | null }
|
|
|
|
> {
|
|
|
|
constructor(props: ErrorBoundaryProps) {
|
|
|
|
super(props)
|
|
|
|
this.state = { error: null }
|
|
|
|
}
|
|
|
|
|
|
|
|
static getDerivedStateFromError(error: Error) {
|
|
|
|
return { error }
|
|
|
|
}
|
|
|
|
|
|
|
|
reset = () => {
|
|
|
|
this.setState({ error: null })
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
if (this.state.error) {
|
|
|
|
return (
|
2022-11-08 14:49:13 +01:00
|
|
|
<>
|
|
|
|
{this.props.errorStyles}
|
|
|
|
<this.props.errorComponent
|
|
|
|
error={this.state.error}
|
|
|
|
reset={this.reset}
|
|
|
|
/>
|
|
|
|
</>
|
2022-10-11 11:17:10 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.props.children
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Renders error boundary with the provided "errorComponent" property as the fallback.
|
|
|
|
* If no "errorComponent" property is provided it renders the children without an error boundary.
|
|
|
|
*/
|
|
|
|
export function ErrorBoundary({
|
|
|
|
errorComponent,
|
2022-11-08 14:49:13 +01:00
|
|
|
errorStyles,
|
2022-10-11 11:17:10 +02:00
|
|
|
children,
|
|
|
|
}: ErrorBoundaryProps & { children: React.ReactNode }): JSX.Element {
|
|
|
|
if (errorComponent) {
|
|
|
|
return (
|
2022-11-08 14:49:13 +01:00
|
|
|
<ErrorBoundaryHandler
|
|
|
|
errorComponent={errorComponent}
|
|
|
|
errorStyles={errorStyles}
|
|
|
|
>
|
2022-10-11 11:17:10 +02:00
|
|
|
{children}
|
|
|
|
</ErrorBoundaryHandler>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return <>{children}</>
|
|
|
|
}
|
|
|
|
|
2022-12-07 17:34:25 +01:00
|
|
|
const styles = {
|
2022-10-11 11:17:10 +02:00
|
|
|
error: {
|
|
|
|
fontFamily:
|
|
|
|
'-apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif',
|
|
|
|
height: '100vh',
|
|
|
|
textAlign: 'center',
|
|
|
|
display: 'flex',
|
|
|
|
flexDirection: 'column',
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'center',
|
|
|
|
},
|
|
|
|
desc: {
|
|
|
|
display: 'inline-block',
|
|
|
|
textAlign: 'left',
|
|
|
|
lineHeight: '49px',
|
|
|
|
height: '49px',
|
|
|
|
verticalAlign: 'middle',
|
|
|
|
},
|
2022-12-07 17:34:25 +01:00
|
|
|
text: {
|
2022-10-11 11:17:10 +02:00
|
|
|
fontSize: '14px',
|
|
|
|
fontWeight: 'normal',
|
|
|
|
lineHeight: '49px',
|
|
|
|
margin: 0,
|
|
|
|
padding: 0,
|
|
|
|
},
|
2022-12-07 17:34:25 +01:00
|
|
|
} as const
|
2022-10-11 11:17:10 +02:00
|
|
|
|
2022-12-07 17:34:25 +01:00
|
|
|
export function GlobalErrorComponent({ error }: { error: any }) {
|
2022-10-11 11:17:10 +02:00
|
|
|
return (
|
|
|
|
<html>
|
2022-12-07 17:34:25 +01:00
|
|
|
<head></head>
|
2022-10-11 11:17:10 +02:00
|
|
|
<body>
|
|
|
|
<div style={styles.error}>
|
|
|
|
<div style={styles.desc}>
|
2022-12-07 17:34:25 +01:00
|
|
|
<h2 style={styles.text}>
|
2022-10-11 11:17:10 +02:00
|
|
|
Application error: a client-side exception has occurred (see the
|
|
|
|
browser console for more information).
|
|
|
|
</h2>
|
2022-12-07 17:34:25 +01:00
|
|
|
{error?.digest && (
|
|
|
|
<p style={styles.text}>{`Digest: ${error.digest}`}</p>
|
|
|
|
)}
|
2022-10-11 11:17:10 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
)
|
|
|
|
}
|