React tips — Error Handling

Leonardo Bruno Lima
3 min readMay 22, 2018

--

Photo by Jerin J on Unsplash

Error handling in Javascript isn’t complex, but when we are writing React components we may have some difficulties dealing with uncaught errors because try/catch only works for imperative code, but React components are declarative.

Since early React development (≤15), it was common for a child component throwing an error to break the entire application and React did not provide a way to handle them gracefully in components. That’s why Error Boundaries are a good thing.

Error boundaries preserve the declarative nature of React, and behave as you would expect. They are React components that catch JavaScript errors anywhere in their child component tree. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.

In React 16 we have a built-in function called componentDidCatch to deal with this uncaught errors. Let’s take a look:

This is a component that will throw an error when counter hits 5.

class BuggyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(({counter}) => ({
counter: counter + 1
}));
}

render() {
if (this.state.counter === 5) {
// Simulate an error
throw new Error('Opss!');
}
return <button onClick={this.handleClick}>{this.state.counter}</button>;
}
}

This is the ErrorBoundary component where we can use the componentDidCatch method.

class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null, errorInfo: null };
}

componentDidCatch(error, errorInfo) {
this.setState({
error: error,
errorInfo: errorInfo
});
}

render() {
if (this.state.errorInfo) {
return (
<div>
<h2>Something went wrong</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
// Render children if there's no error
return this.props.children;
}
}

App.js

function App() {
return (
<div>
<p>
<b>
This is an example of error boundaries in React 16.x
<br /><br />
</b>
</p>
<hr />
<ErrorBoundary>
<p>These components are inside the same error boundary. If one crashes, the error boundary will replace both of them.</p>
<BuggyComponent />
<BuggyComponent />
</ErrorBoundary>
<hr />
<p>These two counters are each inside of their own error boundary. So if one crashes, the other is not affected.</p>
<ErrorBoundary><BuggyComponent /></ErrorBoundary>
<ErrorBoundary><BuggyComponent /></ErrorBoundary>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);

If you run this app you can see this:

Now, click on the buttons more than 5 times you can see the errors. For the first two buttons if one crashes, the error boundary will replace both of them because they are inside the same error boundary. For the others two buttons they are each inside of their own error boundary. So if one crashes, the other is not affected.

As you can see, the granularity of error boundaries is up to you. You may wrap top-level route components to display a custom menssage or you may wrap individual components in an error boundary to protect them from crashing the rest of the application.

That’s all folks,

Thanks for reading!

--

--