Error Boundaries were introduced in React v16 as a way to catch tricky errors that occur during the render phase. In the past, this would have caused the app to unmount completely, and the user would just see a blank web page, which is not ideal!
In this article, you’ll learn about Error Boundaries via code snippets and interactive demos.
To follow along with this article, you will need:
This tutorial was verified with Node v16.4.2, npm
v7.19.1, and react
v17.0.2.
We will inevitably encounter unexpected errors in our apps during development. You could be trying to access a deeply-nested property on an object that does not exist, or sometimes it is not in your control (like a failed HTTP request to a third-party API).
In the demo below, we will simulate an error to see what normally happens without an Error Boundary.
import React from 'react';
class BuggyCounter extends React.Component {
state = {
counter: 0,
};
handleClick = () => {
this.setState({
counter: this.state.counter + 1,
});
};
render() {
if (this.state.counter === 5) {
// Simulate an error!
throw new Error('Simulated error.');
}
return (
<div>
<h1>{this.state.counter}</h1>
<button onClick={this.handleClick}>+</button>
</div>
);
}
}
export default BuggyCounter;
Visit the live code example alligatorio-react-error-boundaries-1
by @wle8300 on CodePen.
Click the + (increment) button and observe how it fails at 5.
OutputUncaught Error: Simulated error.
When the app encounters an error, the component completely unmounts itself and the user is left with a blank HTML page. This can leave users feeling confused and they will not know what to do next.
Error Boundaries provide a way to gracefully handle these errors!
What are Error Boundaries exactly? Contrary to what you may think, it’s not a new component or JavaScript library. It’s more like a strategy for handling errors in React components.
Specifically, it is the usage of two methods that are available in React components:
import React from 'react';
class MyErrorBoundaryExample extends React.Component {
state = {
error: null,
};
static getDerivedStateFromError(error) {
// Update state so next render shows fallback UI.
return { error: error };
}
componentDidCatch(error, info) {
// Log the error to an error reporting service
logErrorToExampleService(error, info);
}
render() {
if (this.state.error) {
// You can render any custom fallback UI
return <p>Something broke</p>;
}
return this.props.children;
}
}
export default MyErrorBoundaryExample;
static getDerivedStateFromError
is a lifecycle method that allows the Error Boundary a chance to update the state
and thus triggering a last render()
. In the above code snippet, the state is used to reveal a human-friendly error message instead of the broken component (e.g., this.props.children
).componentDidCatch
is a lifecycle method designed for triggering side-effects (e.g., logging the error to tools like Crashlytics). You can access info.componentStack
to get a developer-friendly stack trace that will be useful for triaging the bug.Any React Component is considered an Error Boundary when it employs at least one of these lifecycle methods.
Good practices suggest that you will want to create a component that is purpose-built as an Error Boundary instead of mixing error-handling logic into your generic components.
Let’s slightly modify <MyErrorBoundary>
, and then wrap it around <BuggyComponent>
so it will catch the error!
import React from 'react';
class MyErrorBoundary extends React.Component {
state = {
errorMessage: '',
};
static getDerivedStateFromError(error) {
return { errorMessage: error.toString() };
}
componentDidCatch(error, info) {
this.logErrorToServices(error.toString(), info.componentStack);
}
// A fake logging service.
logErrorToServices = console.log;
render() {
if (this.state.errorMessage) {
return <p>{this.state.errorMessage}</p>;
}
return this.props.children;
}
}
export default MyErrorBoundary;
And App.js
:
import React from 'react';
import BuggyCounter from './BuggyCounter';
import MyErrorBoudnary from './MyErrorBoundary';
class App extends React.Component {
refreshPage = () => {
history.go(0);
};
render() {
return (
<div>
<MyErrorBoundary>
<BuggyCounter />
</MyErrorBoundary>
<hr />
<button onClick={this.refreshPage}>Refresh Page</button>
</div>
);
}
}
export default App;
BuggyCounter.js
remains the same.
Visit the live code example alligatorio-react-error-boundaries-2
by @wle8300 on CodePen.
Try clicking the + (increment) button again. Once it reaches 5, it will crash gracefully. Additionally, you can open your console to view a stack trace!
Instead of completely crashing, we can use Error Boundaries to substitute a fallback UI. This provides visual feedback to the user that something broke while allowing them to continue interacting with our app.
They can choose to navigate away or even reach out to customer service to help resolve their situation! It is a great way to redeem an otherwise unfortunate user experience.
Try...Catch
Error Boundaries actually aren’t in direct competition with try...catch
statements. Error Boundaries are only designed for intercepting errors that originate from 3 places in a React component:
render
phaseconstructor
Basically… the React-y parts of a component.
As a counterpoint, these are the places where Error Boundaries will not be able to catch an error:
onClick
, onChange
, etc.)setTimeout
or requestAnimationFramecallbacks
So Error Boundaries don’t really impact how you use try...catch
. They’re both needed as a robust strategy for handling errors in React.
In this article, you learned about Error Boundaries.
Note: Error Boundaries are only available in Class-based React Components. At the time of this writing, there is currently not a way to implement it using React Hooks.
Now that Error Boundaries are available since React version 16, it’s generally advisable to use at least one Error Boundary at the root of your app (e.g., the App.js
file). This will prevent users from seeing a blank HTML page and perhaps see a nice fallback UI instead.
Going forward, you can employ several different kinds of Error Boundaries that employ different fallback UIs or those that only log errors to a third-party service.
Check out the official React docs for more info!
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!