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
22
js/app.js
22
js/app.js
@ -5,8 +5,6 @@ require('babel/polyfill');
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Router, Redirect } from 'react-router';
|
import { Router, Redirect } from 'react-router';
|
||||||
import history from './history';
|
import history from './history';
|
||||||
import { ReactError, ResourceNotFoundError } from './react_error.js';
|
|
||||||
import { ResourceNotFoundErrorHandler } from './react_error_handlers';
|
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import fetch from 'isomorphic-fetch';
|
import fetch from 'isomorphic-fetch';
|
||||||
@ -93,20 +91,12 @@ class AppGateway {
|
|||||||
// us in that case.
|
// us in that case.
|
||||||
history.listen(EventActions.routeDidChange);
|
history.listen(EventActions.routeDidChange);
|
||||||
|
|
||||||
ReactError({
|
React.render((
|
||||||
render: React.render,
|
<Router history={history}>
|
||||||
params: [(
|
{redirectRoute}
|
||||||
<Router history={history}>
|
{getRoutes(type, subdomain)}
|
||||||
{redirectRoute}
|
</Router>
|
||||||
{getRoutes(type, subdomain)}
|
), document.getElementById('main'));
|
||||||
</Router>
|
|
||||||
),
|
|
||||||
document.getElementById('main')],
|
|
||||||
errors: [{
|
|
||||||
error: ResourceNotFoundError,
|
|
||||||
handler: ResourceNotFoundErrorHandler
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
// Send the applicationDidBoot event to the third-party stores
|
// Send the applicationDidBoot event to the third-party stores
|
||||||
EventActions.applicationDidBoot(settings);
|
EventActions.applicationDidBoot(settings);
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { History } from 'react-router';
|
import { History } from 'react-router';
|
||||||
|
|
||||||
|
import ReactError from '../../mixins/react_error';
|
||||||
|
import { ResourceNotFoundError } from '../../models/errors';
|
||||||
|
|
||||||
import EditionActions from '../../actions/edition_actions';
|
import EditionActions from '../../actions/edition_actions';
|
||||||
import EditionStore from '../../stores/edition_store';
|
import EditionStore from '../../stores/edition_store';
|
||||||
|
|
||||||
@ -10,6 +13,7 @@ import Edition from './edition';
|
|||||||
|
|
||||||
import AscribeSpinner from '../ascribe_spinner';
|
import AscribeSpinner from '../ascribe_spinner';
|
||||||
|
|
||||||
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
import { setDocumentTitle } from '../../utils/dom_utils';
|
import { setDocumentTitle } from '../../utils/dom_utils';
|
||||||
|
|
||||||
|
|
||||||
@ -21,7 +25,7 @@ let EditionContainer = React.createClass({
|
|||||||
params: React.PropTypes.object
|
params: React.PropTypes.object
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [History],
|
mixins: [History, ReactError],
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return EditionStore.getState();
|
return EditionStore.getState();
|
||||||
@ -33,14 +37,7 @@ let EditionContainer = React.createClass({
|
|||||||
// as it will otherwise display wrong/old data once the user loads
|
// as it will otherwise display wrong/old data once the user loads
|
||||||
// the edition detail a second time
|
// the edition detail a second time
|
||||||
EditionActions.updateEdition({});
|
EditionActions.updateEdition({});
|
||||||
|
|
||||||
EditionStore.listen(this.onChange);
|
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();
|
this.loadEdition();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -57,9 +54,7 @@ let EditionContainer = React.createClass({
|
|||||||
const { editionError } = this.state;
|
const { editionError } = this.state;
|
||||||
|
|
||||||
if(editionError && editionError.status === 404) {
|
if(editionError && editionError.status === 404) {
|
||||||
// Even though the /404 path doesn't really exist,
|
this.throws(new ResourceNotFoundError(getLangText('Ups, the edition you\'re looking for doesn\'t exist.')));
|
||||||
// we can still redirect there and the page will show up
|
|
||||||
this.history.pushState(null, '/404');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -4,6 +4,9 @@ import React from 'react';
|
|||||||
import { History } from 'react-router';
|
import { History } from 'react-router';
|
||||||
import Moment from 'moment';
|
import Moment from 'moment';
|
||||||
|
|
||||||
|
import ReactError from '../../mixins/react_error';
|
||||||
|
import { ResourceNotFoundError } from '../../models/errors';
|
||||||
|
|
||||||
import PieceActions from '../../actions/piece_actions';
|
import PieceActions from '../../actions/piece_actions';
|
||||||
import PieceStore from '../../stores/piece_store';
|
import PieceStore from '../../stores/piece_store';
|
||||||
|
|
||||||
@ -53,7 +56,7 @@ let PieceContainer = React.createClass({
|
|||||||
params: React.PropTypes.object
|
params: React.PropTypes.object
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [History],
|
mixins: [History, ReactError],
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return mergeOptions(
|
return mergeOptions(
|
||||||
@ -91,9 +94,7 @@ let PieceContainer = React.createClass({
|
|||||||
const { pieceError } = this.state;
|
const { pieceError } = this.state;
|
||||||
|
|
||||||
if(pieceError && pieceError.status === 404) {
|
if(pieceError && pieceError.status === 404) {
|
||||||
// Even though this path doesn't exist we can redirect
|
this.throws(new ResourceNotFoundError(getLangText('Ups, the piece you\'re looking for doesn\'t exist.')));
|
||||||
// to it as it catches all unknown paths
|
|
||||||
this.history.pushState(null, '/404');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -6,6 +6,16 @@ import { getLangText } from '../utils/lang_utils';
|
|||||||
|
|
||||||
|
|
||||||
let ErrorNotFoundPage = React.createClass({
|
let ErrorNotFoundPage = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
message: React.PropTypes.string
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps() {
|
||||||
|
return {
|
||||||
|
message: getLangText('Ups, the page you are looking for does not exist.')
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
@ -13,7 +23,7 @@ let ErrorNotFoundPage = React.createClass({
|
|||||||
<div className="error-wrapper">
|
<div className="error-wrapper">
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
<p>
|
<p>
|
||||||
{getLangText('Ups, the page you are looking for does not exist.')}
|
{this.props.message}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</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