diff --git a/src/common/components/ReloadOnError.jsx b/src/common/components/ReloadOnError.jsx new file mode 100644 index 0000000..cd6ee39 --- /dev/null +++ b/src/common/components/ReloadOnError.jsx @@ -0,0 +1,40 @@ +import { Component } from 'react'; +import PropTypes from 'prop-types'; + +import { NewRelicLoggingService } from '@edx/frontend-logging'; + +/* + Error boundary component used to log caught errors and reload the page. +*/ +export default class ReloadOnError extends Component { + constructor(props) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError() { + // Update state so the next render will show the fallback UI. + return { hasError: true }; + } + + componentDidCatch(error, info) { + NewRelicLoggingService.logError(`${error} ${info}`); + } + + render() { + if (this.state.hasError) { + // Reload the page so the user is not stuck with a broken app. + window.location.reload(); + } + + return this.props.children; + } +} + +ReloadOnError.propTypes = { + children: PropTypes.node, +}; + +ReloadOnError.defaultProps = { + children: null, +}; diff --git a/src/common/index.js b/src/common/index.js index ad2a964..4d7b214 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -1,12 +1,14 @@ import * as utils from './utils'; import Alert from './components/Alert'; import PageLoading from './components/PageLoading'; +import ReloadOnError from './components/ReloadOnError'; import SwitchContent from './components/SwitchContent'; import { configureUserAccountApiService, fetchUserAccount } from './actions'; export { Alert, PageLoading, + ReloadOnError, SwitchContent, utils, configureUserAccountApiService, diff --git a/src/components/App.jsx b/src/components/App.jsx index 4005f43..1ae5a30 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -17,7 +17,7 @@ import { } from '@fortawesome/free-brands-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { fetchUserAccount } from '../common'; +import { ReloadOnError, fetchUserAccount } from '../common'; import { ConnectedAccountSettingsPage } from '../account-settings'; import FooterLogo from '../assets/edx-footer.png'; @@ -179,17 +179,19 @@ class App extends Component { render() { return ( - - - - - - - + + + + + + + + + ); } }