mirror of
https://github.com/ascribe/onion.git
synced 2025-01-03 10:25:08 +01:00
Introduce ReactError mixin for handling errors in React components
This commit is contained in:
parent
68f41f7550
commit
0b2bc1c57b
14
js/app.js
14
js/app.js
@ -5,8 +5,6 @@ require('babel/polyfill');
|
||||
import React from 'react';
|
||||
import { Router, Redirect } from 'react-router';
|
||||
import history from './history';
|
||||
import { ReactError, ResourceNotFoundError } from './react_error.js';
|
||||
import { ResourceNotFoundErrorHandler } from './react_error_handlers';
|
||||
|
||||
/* eslint-disable */
|
||||
import fetch from 'isomorphic-fetch';
|
||||
@ -93,20 +91,12 @@ class AppGateway {
|
||||
// us in that case.
|
||||
history.listen(EventActions.routeDidChange);
|
||||
|
||||
ReactError({
|
||||
render: React.render,
|
||||
params: [(
|
||||
React.render((
|
||||
<Router history={history}>
|
||||
{redirectRoute}
|
||||
{getRoutes(type, subdomain)}
|
||||
</Router>
|
||||
),
|
||||
document.getElementById('main')],
|
||||
errors: [{
|
||||
error: ResourceNotFoundError,
|
||||
handler: ResourceNotFoundErrorHandler
|
||||
}]
|
||||
});
|
||||
), document.getElementById('main'));
|
||||
|
||||
// Send the applicationDidBoot event to the third-party stores
|
||||
EventActions.applicationDidBoot(settings);
|
||||
|
@ -3,6 +3,9 @@
|
||||
import React from 'react';
|
||||
import { History } from 'react-router';
|
||||
|
||||
import ReactError from '../../mixins/react_error';
|
||||
import { ResourceNotFoundError } from '../../models/errors';
|
||||
|
||||
import EditionActions from '../../actions/edition_actions';
|
||||
import EditionStore from '../../stores/edition_store';
|
||||
|
||||
@ -10,6 +13,7 @@ import Edition from './edition';
|
||||
|
||||
import AscribeSpinner from '../ascribe_spinner';
|
||||
|
||||
import { getLangText } from '../../utils/lang_utils';
|
||||
import { setDocumentTitle } from '../../utils/dom_utils';
|
||||
|
||||
|
||||
@ -21,7 +25,7 @@ let EditionContainer = React.createClass({
|
||||
params: React.PropTypes.object
|
||||
},
|
||||
|
||||
mixins: [History],
|
||||
mixins: [History, ReactError],
|
||||
|
||||
getInitialState() {
|
||||
return EditionStore.getState();
|
||||
@ -33,14 +37,7 @@ let EditionContainer = React.createClass({
|
||||
// as it will otherwise display wrong/old data once the user loads
|
||||
// the edition detail a second time
|
||||
EditionActions.updateEdition({});
|
||||
|
||||
EditionStore.listen(this.onChange);
|
||||
|
||||
// Every time we enter the edition detail page, just reset the edition
|
||||
// store as it will otherwise display wrong/old data once the user loads
|
||||
// the edition detail a second time
|
||||
EditionActions.updateEdition({});
|
||||
|
||||
this.loadEdition();
|
||||
},
|
||||
|
||||
@ -57,9 +54,7 @@ let EditionContainer = React.createClass({
|
||||
const { editionError } = this.state;
|
||||
|
||||
if(editionError && editionError.status === 404) {
|
||||
// Even though the /404 path doesn't really exist,
|
||||
// we can still redirect there and the page will show up
|
||||
this.history.pushState(null, '/404');
|
||||
this.throws(new ResourceNotFoundError(getLangText('Ups, the edition you\'re looking for doesn\'t exist.')));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -4,6 +4,9 @@ import React from 'react';
|
||||
import { History } from 'react-router';
|
||||
import Moment from 'moment';
|
||||
|
||||
import ReactError from '../../mixins/react_error';
|
||||
import { ResourceNotFoundError } from '../../models/errors';
|
||||
|
||||
import PieceActions from '../../actions/piece_actions';
|
||||
import PieceStore from '../../stores/piece_store';
|
||||
|
||||
@ -53,7 +56,7 @@ let PieceContainer = React.createClass({
|
||||
params: React.PropTypes.object
|
||||
},
|
||||
|
||||
mixins: [History],
|
||||
mixins: [History, ReactError],
|
||||
|
||||
getInitialState() {
|
||||
return mergeOptions(
|
||||
@ -91,9 +94,7 @@ let PieceContainer = React.createClass({
|
||||
const { pieceError } = this.state;
|
||||
|
||||
if(pieceError && pieceError.status === 404) {
|
||||
// Even though this path doesn't exist we can redirect
|
||||
// to it as it catches all unknown paths
|
||||
this.history.pushState(null, '/404');
|
||||
this.throws(new ResourceNotFoundError(getLangText('Ups, the piece you\'re looking for doesn\'t exist.')));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -6,6 +6,16 @@ import { getLangText } from '../utils/lang_utils';
|
||||
|
||||
|
||||
let ErrorNotFoundPage = React.createClass({
|
||||
propTypes: {
|
||||
message: React.PropTypes.string
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
message: getLangText('Ups, the page you are looking for does not exist.')
|
||||
};
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="row">
|
||||
@ -13,7 +23,7 @@ let ErrorNotFoundPage = React.createClass({
|
||||
<div className="error-wrapper">
|
||||
<h1>404</h1>
|
||||
<p>
|
||||
{getLangText('Ups, the page you are looking for does not exist.')}
|
||||
{this.props.message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
12
js/mixins/react_error.js
Normal file
12
js/mixins/react_error.js
Normal file
@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
import invariant from 'invariant';
|
||||
|
||||
const ReactError = {
|
||||
throws(err) {
|
||||
invariant(err.handler, 'You need to specify a `handler` for this error');
|
||||
err.handler(this, err);
|
||||
}
|
||||
};
|
||||
|
||||
export default ReactError;
|
24
js/models/errors.js
Normal file
24
js/models/errors.js
Normal file
@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import ErrorNotFoundPage from '../components/error_not_found_page';
|
||||
|
||||
|
||||
export class ResourceNotFoundError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
Error.captureStackTrace(this, this.constructor.name);
|
||||
}
|
||||
|
||||
handler(component, err) {
|
||||
if(!component.state._monkeyPatched) {
|
||||
component.render = () => <ErrorNotFoundPage message={err.message} />;
|
||||
component.setState({
|
||||
_monkeyPatched: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
function _transformErrorsListToHashMap(errors) {
|
||||
return errors.reduce((errObj, { error, handler }) => {
|
||||
const errorName = error.name;
|
||||
if(!errObj[errorName]) {
|
||||
errObj[errorName] = {
|
||||
error: error,
|
||||
handler: handler
|
||||
};
|
||||
return errObj;
|
||||
} else {
|
||||
throw new Error('You\'re trying to override the error handler for ' + errorName + ' by specifying it twice.');
|
||||
}
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function ReactError({ render, params, errors }) {
|
||||
errors = _transformErrorsListToHashMap(errors);
|
||||
try {
|
||||
// use react's render function + parameters to render
|
||||
// the application
|
||||
render(...params);
|
||||
} catch(err) {
|
||||
const potErrorRoutine = errors[err.name];
|
||||
if(potErrorRoutine) {
|
||||
potErrorRoutine.handler(err);
|
||||
} else {
|
||||
console.error(err.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Followed: http://stackoverflow.com/a/32749533/1263876 for extending errors
|
||||
export class ResourceNotFoundError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
Error.captureStackTrace(this, this.constructor.name);
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
export function ResourceNotFoundErrorHandler() {
|
||||
console.log(arguments);
|
||||
}
|
Loading…
Reference in New Issue
Block a user