-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): add ErrorBoundary component
catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed improves #33
- Loading branch information
1 parent
1716fe6
commit c6146f6
Showing
3 changed files
with
132 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// @flow strict | ||
import React, { Component } from 'react'; | ||
import type { ComponentType } from 'react'; | ||
|
||
import ErrorBoundaryFallbackComponent from './ErrorBoundaryFallbackComponent'; | ||
|
||
type Props = { | ||
children?: *, | ||
FallbackComponent: ComponentType<*>, | ||
onError?: (error: Error, componentStack: string) => void, | ||
}; | ||
|
||
type ErrorInfo = { | ||
componentStack: string, | ||
}; | ||
|
||
type State = { | ||
error: ?Error, | ||
info: ?ErrorInfo, | ||
}; | ||
|
||
class ErrorBoundary extends Component<Props, State> { | ||
static defaultProps = { | ||
FallbackComponent: ErrorBoundaryFallbackComponent, | ||
}; | ||
|
||
state = { | ||
error: null, | ||
info: null, | ||
}; | ||
|
||
componentDidCatch(error: Error, info: ErrorInfo): void { | ||
const { onError } = this.props; | ||
|
||
if (typeof onError === 'function') { | ||
try { | ||
onError.call(this, error, info ? info.componentStack : ''); | ||
// eslint-disable-next-line no-empty | ||
} catch (ignoredError) {} | ||
} | ||
|
||
this.setState({ error, info }); | ||
} | ||
|
||
render() { | ||
const { children, FallbackComponent } = this.props; | ||
const { error, info } = this.state; | ||
|
||
if (error != null) { | ||
return ( | ||
<FallbackComponent | ||
componentStack={ | ||
info ? info.componentStack : '' | ||
} | ||
error={error} | ||
/> | ||
); | ||
} | ||
|
||
return children || null; | ||
} | ||
} | ||
|
||
export const withErrorBoundary = ( | ||
SomeComponent: ComponentType<*>, | ||
FallbackComponent: ComponentType<*>, | ||
onError: (Error) => void, | ||
): ComponentType<{}> => { | ||
const Wrapped = (props) => ( | ||
<ErrorBoundary | ||
FallbackComponent={FallbackComponent} onError={onError} | ||
> | ||
<SomeComponent {...props} /> | ||
</ErrorBoundary> | ||
); | ||
|
||
// Format for display in DevTools | ||
const name = SomeComponent.displayName || SomeComponent.name; | ||
|
||
Wrapped.displayName = name | ||
? `WithErrorBoundary(${name})` | ||
: 'WithErrorBoundary'; | ||
|
||
return Wrapped; | ||
}; | ||
|
||
export default ErrorBoundary; |
38 changes: 38 additions & 0 deletions
38
src/app/components/core/ErrorBoundary/ErrorBoundaryFallbackComponent.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// @flow strict | ||
|
||
import React from 'react'; | ||
|
||
type Props = { | ||
componentStack: string, | ||
error: Error, | ||
}; | ||
|
||
const toTitle = ( | ||
error: Error, | ||
componentStack: string, | ||
): string => (`${error.toString()}\n\nThis is located at:${componentStack}`); | ||
|
||
const style = { | ||
alignItems: 'center', | ||
boxSizing: 'border-box', | ||
color: '#FFF', | ||
cursor: 'help', | ||
display: 'flex', | ||
flexDirection: 'column', | ||
height: '100%', | ||
maxHeight: '100vh', | ||
maxWidth: '100vw', | ||
textAlign: 'center', | ||
width: '100%', | ||
}; | ||
|
||
const ErrorBoundaryFallbackComponent = ({ componentStack, error }: Props) => ( | ||
<div | ||
style={style} | ||
title={toTitle(error, componentStack)} | ||
> | ||
<pre>{componentStack}</pre> | ||
</div> | ||
); | ||
|
||
export default ErrorBoundaryFallbackComponent; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// @flow strict | ||
import ErrorBoundaryFallbackComponent from './ErrorBoundaryFallbackComponent'; | ||
import ErrorBoundary, { withErrorBoundary } from './ErrorBoundary'; | ||
|
||
export default ErrorBoundary; | ||
|
||
export { ErrorBoundary, withErrorBoundary, ErrorBoundaryFallbackComponent }; |