Use Error Boundaries

Hi,

Does someone know how to use Error Boundaries of React in Meteor with Server Side Rendering ? The simpliest example here doesn’t work.

When I throw an error in a render function of React

  render() {
    throw new Error("trol");
    return (
      <h1>Hey !</h1>
    )
  }

I have the following error

Error running template: Error: trol

The same if I use Meteor.Error

I have a class ErrorBoundary

import React from 'react';

export default class ErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }

    componentDidCatch(error, info) {
        // Display fallback UI
        this.setState({ hasError: true });
        // You can also log the error to an error reporting service
        logErrorToMyService(error, info);
    }

    render() {
        if (this.state.hasError) {
            // You can render any custom fallback UI
            return <h1>Something went wrong.</h1>;
        }
        return this.props.children;
    }
}

My client/main.js

const preloadedState = window.__PRELOADED_STATE__;
delete window.__PRELOADED_STATE__;
const store = createStore(appReducer, preloadedState, applyMiddleware(thunk));

const browserHistory = createBrowserHistory();

export const renderRoutes = () => (
    <Provider store={store}>
        <Router history={browserHistory}>
            <Routes/>
        </Router>
    </Provider>
);

Meteor.startup(() => {
    hydrate(renderRoutes() , document.getElementById('render-target'));
});

The Router


const Routes = () => {
    return (
        <ErrorBoundary>
            <Switch>
                <Route exact path="/" component={List}/>
                <Route exact path="/article/add" component={Add}/>
                <Route exact path="/article/edit/:slug" component={Edit}/>
                <Route exact path="/article/:slug" component={Single}/>
                <Route component={NotFound}/>
            </Switch>
        </ErrorBoundary>
    )
};

export default Routes;

And my List file where I throw an error

class Article extends Component {
    render() {
        return (
            <li>
                <h2>{this.props.article.title}</h2>
                <p>{this.props.article.text}</p>
                <Link to={"/article/edit/" + this.props.article.slug}>Edit</Link>
            </li>
        );
    }
}

// App component - represents the whole app
class List extends Component {
    constructor(props) {
        super(props);
        this.props.articleActions.getArticles(this.props.articles);
    }

    renderArticles() {
        return this.props.articles.map((article) => (
            <Article key={article._id} article={article} />
        ));
    }

    render() {
        throw new Error("trol");
        return (
            <ul>
                {this.renderArticles()}
            </ul>
        );
    }
}

function mapStateToProps(state) {
    return {
        articles: state.articles
    }
}

function mapDispatchToProps(dispatch) {
    return {
        articleActions: bindActionCreators(articleActionCreators, dispatch)
    }
}

const Tracker = withTracker((data) => {
    if (Meteor.isClient) {
        Meteor.subscribe('articles');
    }
    var articles = Articles.find({}, {title:1, text:1}).fetch();
    if(articles.length == 0 && data.articles) articles = data.articles;
    return {
        articles: articles,
    };
})(List);

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Tracker);

My server/main.js

import { Meteor } from 'meteor/meteor';
import React from "react";
import { StaticRouter, Route } from 'react-router'
import { renderToString } from "react-dom/server";
import { onPageLoad } from "meteor/server-render";
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import Routes from '../imports/router';
import appReducer from '../imports/redux/reducers';
import '../imports/api/tags.js';
import '../imports/api/articles.js';

const store = createStore(appReducer, applyMiddleware(thunk));

onPageLoad(sink => {
    const context = {};
    const html = "<div id='render-target'>" + renderToString(
        <Provider store={store}>
            <StaticRouter location={sink.request.url.path} context={context}>
                <Routes/>
            </StaticRouter>
        </Provider>
    ) + "</div>";
    const preloadedState = store.getState();
    sink.appendToBody(html);
    sink.appendToBody("<script>window.__PRELOADED_STATE__  = "+JSON.stringify(preloadedState)+"</script>");
});

Meteor.startup(() => {

});

It seems componentDidCatch doesn’t catch anything because Meteor catches the error before.

Edit : I noticed that it doesn’t work on server rendering. It works if I remove onPageLoad function.

Please describe how it doesn’t work.

Did you setup your error boundary in the above component? Please show us the relevant code so that we can debug the issue.

1 Like

I’m using without any problem.

Where is your componentDidCatch?

I edited my first post. Thanks for your quick answers :grinning:

1 Like