import React from 'react'
import PropTypes from 'prop-types'
import { Button, Container, FloatingBackground, Header, Message } from './styled'
import MappyConcerned from './mappy_concerned.svg'
import MappyDead from './mappy_dead.svg'
/**
* React ErrorBoundary component
* @component
*/
class ErrorBoundary extends React.Component {
constructor (props) {
super(props)
this.state = {
abandonAllHope: false,
attemptedReset: false,
hasError: false
}
}
componentDidCatch () {
const { attemptedReset } = this.state
if (attemptedReset) {
// don't try to reset state after attemptedReset
this.setState({ abandonAllHope: true })
}
}
componentDidUpdate (prevProps, prevState) {
const { abandonAllHope, attemptedReset, hasError } = this.state
// reset the attemptedReset bool whenever successful reset occurs so next error can also be reset instead of abandoned
if (prevState.hasError && !prevState.attemptedReset && !abandonAllHope && attemptedReset && !hasError) {
this.setState({ attemptedReset: false })
}
}
static getDerivedStateFromError (error) { // eslint-disable-line handle-callback-err
return { hasError: true }
}
render () {
const { abandonAllHope, hasError } = this.state
const { floating } = this.props
return hasError
? (
<FloatingBackground floating={floating}>
<Container floating={floating}>
<Header>Something went wrong!</Header>
{!abandonAllHope
? <MappyConcerned />
: <MappyDead />}
<Message>{!abandonAllHope
? 'Have another go?'
: 'This component is beyond recovery...'}
</Message>
{!abandonAllHope
? <Button onClick={() => this.setState({ attemptedReset: true, hasError: false })}>Try Again</Button>
: null}
</Container>
</FloatingBackground>
) : this.props.children
}
}
ErrorBoundary.propTypes = {
/** pass components as children of ErrorBoundary which are rendered if there is no error */
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
]).isRequired,
/** display error boundary with a modal-like background */
floating: PropTypes.bool
}
export default ErrorBoundary