diff --git a/package-lock.json b/package-lock.json index 6a366d6c61a1..68ea17fc20b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,6 +69,7 @@ "react-collapse": "^5.1.0", "react-content-loader": "^6.1.0", "react-dom": "18.1.0", + "react-error-boundary": "^4.0.11", "react-map-gl": "^7.1.3", "react-native": "0.72.3", "react-native-blob-util": "^0.17.3", @@ -40187,6 +40188,17 @@ "react": "^18.1.0" } }, + "node_modules/react-error-boundary": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.11.tgz", + "integrity": "sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/react-freeze": { "version": "1.0.3", "license": "MIT", @@ -75742,6 +75754,14 @@ "scheduler": "^0.22.0" } }, + "react-error-boundary": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.11.tgz", + "integrity": "sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw==", + "requires": { + "@babel/runtime": "^7.12.5" + } + }, "react-freeze": { "version": "1.0.3", "requires": {} diff --git a/package.json b/package.json index 0605d44d89fc..72574455719b 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,7 @@ "react-content-loader": "^6.1.0", "react-dom": "18.1.0", "react-map-gl": "^7.1.3", + "react-error-boundary": "^4.0.11", "react-native": "0.72.3", "react-native-blob-util": "^0.17.3", "react-native-collapsible": "^1.6.0", diff --git a/src/components/ErrorBoundary/BaseErrorBoundary.js b/src/components/ErrorBoundary/BaseErrorBoundary.js index e479b04f7ade..d626442e81dd 100644 --- a/src/components/ErrorBoundary/BaseErrorBoundary.js +++ b/src/components/ErrorBoundary/BaseErrorBoundary.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import {ErrorBoundary} from 'react-error-boundary'; import BootSplash from '../../libs/BootSplash'; import GenericErrorPage from '../../pages/ErrorPage/GenericErrorPage'; @@ -22,40 +23,27 @@ const defaultProps = { * This component captures an error in the child component tree and logs it to the server * It can be used to wrap the entire app as well as to wrap specific parts for more granularity * @see {@link https://reactjs.org/docs/error-boundaries.html#where-to-place-error-boundaries} + * @return {React.Component} */ -class BaseErrorBoundary extends React.Component { - constructor(props) { - super(props); - this.state = {hasError: false}; - this.clearError = this.clearError.bind(this); - } - - static getDerivedStateFromError() { - // Update state so the next render will show the fallback UI. - return {hasError: true}; - } - - componentDidCatch(error, errorInfo) { - this.props.logError(this.props.errorMessage, error, JSON.stringify(errorInfo)); - +function BaseErrorBoundary({logError, errorMessage, children}) { + const catchError = (error, errorInfo) => { + logError(errorMessage, error, JSON.stringify(errorInfo)); // We hide the splash screen since the error might happened during app init BootSplash.hide(); - } - - clearError() { - this.setState({hasError: false}); - } - - render() { - if (this.state.hasError) { - return ; - } - - return this.props.children; - } + }; + + return ( + } + onError={catchError} + > + {children} + + ); } BaseErrorBoundary.propTypes = propTypes; BaseErrorBoundary.defaultProps = defaultProps; +BaseErrorBoundary.displayName = 'BaseErrorBoundary'; export default BaseErrorBoundary; diff --git a/src/pages/ErrorPage/GenericErrorPage.js b/src/pages/ErrorPage/GenericErrorPage.js index 3ff3bc686419..02ab38a1ef20 100644 --- a/src/pages/ErrorPage/GenericErrorPage.js +++ b/src/pages/ErrorPage/GenericErrorPage.js @@ -1,6 +1,6 @@ import React from 'react'; -import PropTypes from 'prop-types'; import {View} from 'react-native'; +import {useErrorBoundary} from 'react-error-boundary'; import Icon from '../../components/Icon'; import defaultTheme from '../../styles/themes/default'; import * as Expensicons from '../../components/Icon/Expensicons'; @@ -19,12 +19,11 @@ import * as StyleUtils from '../../styles/StyleUtils'; const propTypes = { ...withLocalizePropTypes, - - /** Callback to call on refresh button click */ - onRefresh: PropTypes.func.isRequired, }; -function GenericErrorPage(props) { +function GenericErrorPage({translate}) { + const {resetBoundary} = useErrorBoundary(); + return ( {({paddingBottom}) => ( @@ -40,12 +39,12 @@ function GenericErrorPage(props) { /> - {props.translate('genericErrorPage.title')} + {translate('genericErrorPage.title')} - {`${props.translate('genericErrorPage.body.helpTextConcierge')} `} + {`${translate('genericErrorPage.body.helpTextConcierge')} `}