Skip to content

Commit

Permalink
feat(RouteView): React 16 support
Browse files Browse the repository at this point in the history
Introduce Error Boundary component around RouteView.

BREAKING CHANGE: #3:  React 16 support

#3
  • Loading branch information
LeonardoGentile committed Oct 8, 2017
1 parent bcb6373 commit 3ed054c
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 13 deletions.
2 changes: 2 additions & 0 deletions .idea/react-mobx-router5.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ They _observe_ the `mobx-router5` _observables_ and react when they change.

## Requirements

- __react >= 15.0.0__
- __react >= 16.0.0__. For React < 16.0.0 install "^4.0.0" version
- __mobx >= 3.1.0__
- __mobx-react >= 4.0.0__
- __router5 >= 5.0.0__
Expand Down Expand Up @@ -371,6 +371,10 @@ All props not listed below will be passed trough to the new generated component
to avoid possible inconsistencies if the subcomponents are observers of `route` (**TODO**: This use case needs more study)
- `routeNodeName`: the name of the route for the React component from where to re-render (route node)
- `routes`: nested routes configuration array (with the extra `component` field for each route)
- `errorMessage`: a string for custom message to display inside an `h1` in case of error during the component selection. *Optional*, **default: 'Something went wrong.'**
- `errorStyle`: a style object to be applied to the `h1` error description. *Optional*, **default: {color: 'rgb(217, 83, 79)'**

Notice that the RouteView component is internally wrapped with an [Error Boundary Component](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html) (introduced in react 16), this ensure that in case of exception while selecting the component to display the entire app won't crash and an error message will be displayed instead. The error message is rendered inside an `h1`, it is customizabile using the `errorMessage` and `errorStyle` props passed to `routeView`.

**Example**

Expand Down
28 changes: 16 additions & 12 deletions src/modules/RouteView.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { Component } from 'react';
import React, { Component, createElement} from 'react';
import PropTypes from 'prop-types';
import {createElement} from 'react';
import {getComponent} from './utils';


Expand All @@ -18,9 +17,9 @@ function RouteView(props) {
if (!currentRoute) {
throw new Error('RouteView component requires a route prop');
}
const FetchedComponent = getComponent(currentRoute, routeNodeName, routes); // getComponent may throw
const SelectedComponent = getComponent(currentRoute, routeNodeName, routes); // getComponent may throw
// Add `{key: route.meta.id}` to props for a full unmount/mount
return createElement(FetchedComponent, {...passThroughProps, route});
return createElement(SelectedComponent, {...passThroughProps, route});
}

RouteView.displayName = 'RouteView';
Expand All @@ -33,8 +32,7 @@ RouteView.propTypes = {




class ErrorBoundRouteView extends React.Component {
class RouteViewErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
Expand All @@ -59,21 +57,27 @@ class ErrorBoundRouteView extends React.Component {

render() {
if (this.state.hasError) {
return <h1>{this.props.errorMessage || 'Something went wrong.'}</h1>;
return <h1 style={this.props.errorStyle}>{this.props.errorMessage}</h1>;
}
const {errorMessage, ...passThroughProps } = this.props;
return <RouteView {...passThroughProps} />
const {errorMessage, errorStyle, ...passThroughProps } = this.props;
return <RouteView {...passThroughProps} />;
}
}

ErrorBoundRouteView.propTypes = {
RouteViewErrorBoundary.propTypes = {
routes: PropTypes.array.isRequired,
routeNodeName: PropTypes.string.isRequired,
route: PropTypes.object.isRequired,
errorMessage: PropTypes.string
errorMessage: PropTypes.string,
errorStyle: PropTypes.object
};

RouteViewErrorBoundary.defaultProps = {
errorMessage: 'Something went wrong.',
errorStyle: {color: 'rgb(217, 83, 79)'}
};


export default ErrorBoundRouteView;
export default RouteViewErrorBoundary;


35 changes: 35 additions & 0 deletions test/RouteView.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,41 @@ describe('routeNode hoc', () => {
);
expect(renderComp).to.throw();
});

it('should render the default error message with default color when an exception occurs', () => {
const route = { name: 'd.h.l' }; // A route without component

const RouteComp = (props) => (
<RouteView routes={routes} route={route} routeNodeName="d.h"/>
);

const renderedL = mount(
<RouteComp />
);

expect(renderedL.find('h1')).to.have.text('Something went wrong.');
expect(renderedL.find('h1')).to.have.style('color', 'rgb(217, 83, 79)');

});

it('should render a custom error message with a custom style when an exception occurs', () => {
const route = { name: 'd.h.l' }; // A route without component

const RouteComp = (props) => (
<RouteView routes={routes} route={route} routeNodeName="d.h" errorMessage={'Ay, caramba!'} errorStyle={{color: 'rgb(200, 90, 34)', 'font-weight': 'bold' }}/>
);

const renderedL = mount(
<RouteComp />
);

expect(renderedL.find('h1')).to.have.text('Ay, caramba!');
expect(renderedL.find('h1')).to.have.style('color', 'rgb(200, 90, 34)');
expect(renderedL.find('h1')).to.have.style('font-weight', 'bold');

});


});

context('Render the Component', function () {
Expand Down

0 comments on commit 3ed054c

Please sign in to comment.