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.