Render ErrorPage component via app-level error boundary.
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { put, push, call, takeEvery } from 'redux-saga/effects';
|
||||
import { logAPIErrorResponse } from '@edx/frontend-logging';
|
||||
import { put, call, takeEvery } from 'redux-saga/effects';
|
||||
|
||||
import {
|
||||
DELETE_ACCOUNT,
|
||||
@@ -19,8 +18,7 @@ export function* handleDeleteAccount(action) {
|
||||
if (typeof e.response.data === 'string') {
|
||||
yield put(deleteAccountFailure());
|
||||
} else {
|
||||
logAPIErrorResponse(e);
|
||||
yield put(push('/error'));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import { put, call, push, takeEvery } from 'redux-saga/effects';
|
||||
import { logAPIErrorResponse } from '@edx/frontend-logging';
|
||||
import { put, call, takeEvery } from 'redux-saga/effects';
|
||||
|
||||
import { resetPasswordBegin, resetPasswordSuccess, RESET_PASSWORD } from './actions';
|
||||
import { postResetPassword } from './service';
|
||||
|
||||
function* handleResetPassword(action) {
|
||||
try {
|
||||
yield put(resetPasswordBegin());
|
||||
const response = yield call(postResetPassword, action.payload.email);
|
||||
yield put(resetPasswordSuccess(response));
|
||||
} catch (e) {
|
||||
logAPIErrorResponse(e);
|
||||
yield put(push('/error'));
|
||||
}
|
||||
yield put(resetPasswordBegin());
|
||||
const response = yield call(postResetPassword, action.payload.email);
|
||||
yield put(resetPasswordSuccess(response));
|
||||
}
|
||||
|
||||
export default function* saga() {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { call, put, delay, takeEvery, select, all } from 'redux-saga/effects';
|
||||
import { push } from 'connected-react-router';
|
||||
import { logAPIErrorResponse } from '@edx/frontend-logging';
|
||||
|
||||
// Actions
|
||||
import {
|
||||
@@ -54,9 +52,8 @@ export function* handleFetchSettings() {
|
||||
timeZones,
|
||||
}));
|
||||
} catch (e) {
|
||||
logAPIErrorResponse(e);
|
||||
yield put(fetchSettingsFailure(e.message));
|
||||
yield put(push('/error'));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,21 +86,15 @@ export function* handleSaveSettings(action) {
|
||||
if (e.fieldErrors) {
|
||||
yield put(saveSettingsFailure({ fieldErrors: e.fieldErrors }));
|
||||
} else {
|
||||
logAPIErrorResponse(e);
|
||||
yield put(saveSettingsFailure(e.message));
|
||||
yield put(push('/error'));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function* handleFetchTimeZones(action) {
|
||||
try {
|
||||
const response = yield call(ApiService.getTimeZones, action.payload.country);
|
||||
yield put(fetchTimeZonesSuccess(response, action.payload.country));
|
||||
} catch (e) {
|
||||
logAPIErrorResponse(e);
|
||||
yield put(push('/error'));
|
||||
}
|
||||
const response = yield call(ApiService.getTimeZones, action.payload.country);
|
||||
yield put(fetchTimeZonesSuccess(response, action.payload.country));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Component } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { NewRelicLoggingService } from '@edx/frontend-logging';
|
||||
import { logAPIErrorResponse } from '@edx/frontend-logging';
|
||||
|
||||
import ErrorPage from './ErrorPage';
|
||||
|
||||
/*
|
||||
Error boundary component used to log caught errors and reload the page.
|
||||
Error boundary component used to log caught errors and display the error page.
|
||||
*/
|
||||
export default class ReloadOnError extends Component {
|
||||
export default class ErrorBoundary extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
@@ -18,23 +20,22 @@ export default class ReloadOnError extends Component {
|
||||
}
|
||||
|
||||
componentDidCatch(error, info) {
|
||||
NewRelicLoggingService.logError(`${error} ${info}`);
|
||||
logAPIErrorResponse(`${error} ${info}`);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
// Reload the page so the user is not stuck with a broken app.
|
||||
window.location.reload();
|
||||
return <ErrorPage />;
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
ReloadOnError.propTypes = {
|
||||
ErrorBoundary.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
ReloadOnError.defaultProps = {
|
||||
ErrorBoundary.defaultProps = {
|
||||
children: null,
|
||||
};
|
||||
42
src/common/components/ErrorPage.jsx
Normal file
42
src/common/components/ErrorPage.jsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import React, { Component } from 'react';
|
||||
import { FormattedMessage } from '@edx/frontend-i18n';
|
||||
import { Button } from '@edx/paragon';
|
||||
|
||||
export default class ErrorPage extends Component {
|
||||
reload() {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="container-fluid py-5 justify-content-center align-items-start text-center">
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<p className="my-0 py-5 text-muted">
|
||||
<FormattedMessage
|
||||
id="unexpected.error.message.text"
|
||||
defaultMessage="An unexpected error occurred. Please click the button below to return to refresh the page."
|
||||
description="error message when an unexpected error occurs"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<Button
|
||||
buttonType="primary"
|
||||
onClick={this.reload}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="unexpected.error.button.text"
|
||||
defaultMessage="Try Again"
|
||||
description="text for button that tries to reload the app by refreshing the page"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
import * as utils from './utils';
|
||||
import Alert from './components/Alert';
|
||||
import PageLoading from './components/PageLoading';
|
||||
import ReloadOnError from './components/ReloadOnError';
|
||||
import ErrorBoundary from './components/ErrorBoundary';
|
||||
import SwitchContent from './components/SwitchContent';
|
||||
import { configureUserAccountApiService, fetchUserAccount } from './actions';
|
||||
|
||||
export {
|
||||
Alert,
|
||||
ErrorBoundary,
|
||||
PageLoading,
|
||||
ReloadOnError,
|
||||
SwitchContent,
|
||||
utils,
|
||||
configureUserAccountApiService,
|
||||
|
||||
@@ -17,12 +17,11 @@ import {
|
||||
} from '@fortawesome/free-brands-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import { ReloadOnError, fetchUserAccount } from '../common';
|
||||
import { ErrorBoundary, fetchUserAccount } from '../common';
|
||||
import { ConnectedAccountSettingsPage } from '../account-settings';
|
||||
|
||||
import FooterLogo from '../assets/edx-footer.png';
|
||||
import HeaderLogo from '../assets/logo.svg';
|
||||
import ErrorPage from './ErrorPage';
|
||||
import NotFoundPage from './NotFoundPage';
|
||||
|
||||
import messages from './App.messages';
|
||||
@@ -138,7 +137,6 @@ function PageContent({
|
||||
<main>
|
||||
<Switch>
|
||||
<Route exact path="/" component={ConnectedAccountSettingsPage} />
|
||||
<Route path="/error" component={ErrorPage} />
|
||||
<Route path="/notfound" component={NotFoundPage} />
|
||||
<Route path="*" component={NotFoundPage} />
|
||||
</Switch>
|
||||
@@ -179,7 +177,7 @@ class App extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ReloadOnError>
|
||||
<ErrorBoundary>
|
||||
<IntlProvider locale={this.props.locale} messages={getMessages()}>
|
||||
<Provider store={this.props.store}>
|
||||
<ConnectedRouter history={this.props.history}>
|
||||
@@ -191,7 +189,7 @@ class App extends Component {
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
</ReloadOnError>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from '@edx/frontend-i18n';
|
||||
|
||||
export default function ErrorPage() {
|
||||
return (
|
||||
<div className="container-fluid py-5 justify-content-center align-items-start text-center">
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<p className="my-0 py-5 text-muted">
|
||||
<FormattedMessage
|
||||
id="error.unexpected.message"
|
||||
defaultMessage="An unexpected error occurred."
|
||||
description="error message when an unexpected error occurs"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user