This tutorial is out of date and no longer maintained.
If you have built a React app at any point in your programming career, you probably have experienced an error at some point that was cryptic and did not provide any meaningful context on what actually happened.
This probably means the error occurred at some point in the application and our React component did not handle the error gracefully, well mostly because it was none of its business to make sure the entire app holds.
A JavaScript error in a part of the UI shouldn’t break the whole app. To solve this problem for React users, React 16 introduces a new concept of an “error boundary”.
An Error Boundary is a React component that catches errors within its children and does something meaningful with them such as post them to an error logging service or display a fallback UI for the specific child while maintaining the rest of the React app’s sanity.
Therefore, for a block of functionality to be covered by Error Boundaries, it has to be a child of one in the first place.
Before we get started on an example of how you can use React 16, beware that Error Boundaries will not catch errors in:
try / catch
block instead within event handlers.
2. Asynchronous code**Lifecycle methods are special functions that are invoked at different stages in the life of a component. These stages can be categorized into Mounting, Updating, Unmounting and Error handling.
For a component to be considered an Error Boundary, it has to make use of the componentDidCatch()
lifecycle method to handle errors. It works in the same way that Javascript’s try/catch
works.
the componentDidCatch
method is invoked with the error
and info
parameters that contain more context on the thrown error.
Just like the granularity of React component is entirely up to you, Error Boundaries can be as specific as you want them to be. You can have a top-level Error Boundary that prevents the app from crashing due to unexpected occurrences and displaying a more suitable message.
For this article, we will be creating an Error Boundary that only wraps a specific functionality with errors of our own design.
To get started, we will create a simple Component that only allows you to enter up to five characters into an input field. Any more than that and we break the internet. Feel free to try out in of the freely available online editors, I personally use CodePen.io.
class FiveMax extends React.Component {
constructor(props) {
super(props);
this.state = { value: ''}
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({ value: e.target.value})
}
render() {
if(this.state.value.length > 5) {
throw new Error('You cannot enter more than five characters!');
}
return (
<div>
<label>Type away: </label>
<input type="text" value={this.state.value} onChange={this.handleChange} />
</div>
);
}
}
ReactDOM.render(<FiveMax />, document.getElementById('root'));
If you type in more than five characters, you should get a big shiny error on your console.
You will also notice that your input box disappears due to the error. Perfect! Let’s create an Error Boundary. I’ll call mine Shield.
class Shield extends React.Component {
constructor(props) {
super(props);
// Add some default error states
this.state = {
error: false,
info: null,
};
}
componentDidCatch(error, info) {
// Something happened to one of my children.
// Add error to state
this.setState({
error: error,
info: info,
});
}
render() {
if(this.state.error) {
// Some error was thrown. Let's display something helpful to the user
return (
<div>
<h5>Sorry. More than five characters!</h5>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.info.componentStack}
</details>
</div>
);
}
// No errors were thrown. As you were.
return this.props.children;
}
}
Nothing special right? The only new thing that we have in this component that you probably haven’t used before is the componentDidCatch
method.
When an error is thrown in any of its children, the error state will be updated and a meaningful error displayed. Otherwise, the Shield
will go ahead and display the children as usual.
To start using the Error Boundary, we will look at two separate scenarios.
In a situation where you have two Components within the same Error Boundary and an error is thrown in one of the Components, they are both affected due to the structure of the render
method in our Shield
component.
To try this out, we will add two FiveMax
components inside our Shield
Error Boundary.
// Shield Component
// FiveMax Component
function App() {
return (
<div>
<h3>Two children under one error boundary. If one crashes. Both are affected!</h3>
<Shield>
<FiveMax />
<FiveMax />
</Shield>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
When you try typing more than five characters into any of the fields, an error is logged on the console and we get a pleasing and more informative message displayed to the user in place of both components.
This is all good but we did not need to lose the other component that did not throw any errors. Let’s fix that!
Now to prevent what happened in scenario one, we will have each of the FiveMax
components in their own Shield
Error Boundary.
// Shield Component
// FiveMax Component
function App() {
return (
<div>
<h3>Two children, each with their own Error Boundary. One crashes, the other is not affected</h3>
<Shield><FiveMax /></Shield>
<Shield><FiveMax /></Shield>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
Now try typing more than five characters in any of the components. Notice anything? Instead of losing both components to the error, you only lose the Component that is affected. In place of the affected component only. The rest of the App remains intact!
You can try out both scenarios in the Pen below.
See the Pen Five Max by John Kariuki (@johnkariuki) on CodePen.
How you use error logs entirely depends on what you want to achieve. You can have a separate Error Boundary for your navigation bar and a different one for the rest of your app so that if something goes wrong in your App’s functionality, the navigation remains usable!
Remember that if your Error Boundary throws an error, anything in its child component tree is affected. So be careful when coming up with one so that it is not prone to errors.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!