1
0
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:
Tim Daubenschütz 2015-11-30 18:23:03 +01:00
parent 68f41f7550
commit 0b2bc1c57b
8 changed files with 64 additions and 79 deletions

View File

@ -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);

View File

@ -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.')));
}
},

View File

@ -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.')));
}
},

View File

@ -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
View 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
View 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
});
}
}
}

View File

@ -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);
}
}

View File

@ -1,5 +0,0 @@
'use strict';
export function ResourceNotFoundErrorHandler() {
console.log(arguments);
}