React Error Boundaries
Published: ....
Last modified: ....
Share this post on BlueskySee discussion on Bluesky
The other week I got an email from a developer wondering if I could help them learn more about Error Boundaries within React.
I figured that instead of sharing that information directly to them, I would spend a bit more time writing it up in a blog post so others could learn from the content as well. This post is only meant as a brief introduction to the concept behind Error Boundaries within React, for more advanced information refer to the where can I learn more section at the bottom of the page.
So lets dive in!
What are Error Boundaries?
Simply put, you can think of Error Boundaries as try ... catch
blocks within a
component tree. This is an example of a try catch block:
try {
doSomethingThatMayThrow();
} catch (error) {
handleError(error);
}
In that snippet above, the doSomethingThatMayThrow
function can throw
anything (generally an error) and the catch
block will capture this thrown
value and continue script execution.
In React this would look like something like the following:
<ErrorBoundary>
<ComponentThatMayThrow />
</ErrorBoundary>;
Great, so we have a rough understanding of what they look like in practice, but how do they actually work?
How do Error Boundaries Work?
An error boundary can be implemented within any component that uses either the:
componentDidCatch
orgetDerivedStateFromError
lifecycle methods. Both of these methods allow the component to catch an error thrown from its children.
So lets setup a simple generic Error Boundary component.
class ErrorBoundary extends React.Component {
state = {
hasCaughtError: false,
};
componentDidCatch(error) {
this.setState({ hasCaughtError: true });
}
render() {
if (this.state.hasCaughtError) {
return <p>Oh no, an error has occurred</p>;
}
return this.props.children;
}
}
In this component above, whenever some component anywhere within its children
throws an error, React will propagate that error up to this component, and call
its componentDidCatch
lifecycle method with the error that was thrown.
This component can then setState to change the children returned from render in order to recover from that thrown error.
Caveats
As with most things, there are some caveats with this workflow. The first and
most notable, is that this component will only catch errors thrown during the
render
phase. This means that if an error occurs within a callback that is not
a setState
updater, the error boundary component will not catch it.
Another caveat, or feature I guess is that if you do want to catch a particular
error from an event handler higher up in the component tree at your error
boundary, you can throw
from within a setState:
import { Component } from 'react';
export default class Example extends Component {
handleChange = (e) => {
let {
target: { value },
} = e;
this.setState(() => {
if (value.includes('foo')) {
throw new Error('Error!');
}
return null;
});
};
render() {
return (
<input
onChange={this.handleChange}
placeholder='Try typing here!'
style={{
height: 35,
fontSize: 18,
width: 'calc(100% - 2em)',
padding: '1rem',
}}
/>
);
}
}
A note on getDerivedStateFromError
The getDerivedStateFromError
lifecycle was introduced later than the
componentDidCatch
lifecycle, and operates on a similar mechanism as the
getDerivedStateFromProps
lifecycle method in that it is a single pass handler.
This means that React is able to reconcile the error handling within the same
rendering batch as when the error occurs, I think. I don't really understand
what this actually means however, so take this with a grain of salt 🧂 .
Where can I learn more?
As always, the ReactJS first party docs site is hands down one of the best resources out there about React. Here is a great getting started introduction to Error Boundaries on the ReactJS Docs.
Tags:
Related Posts
Web Development
Published: ....
I recently launched a rewrite and redesign of this personal website, I figured I'd talk a bit about the changes and new features that I added along the way!
Published: ....
A quick tip to implementing CSS theming that's compatible with Server Side Rendered applications!
Published: ....
A brief overview on how we launched The Bikeshed Podcast, including a deep dive in our recording and distribution workflows!
Published: ....
A quick tip outlining how to provide specific TypeScript type definitions for a local module!
Published: ....
Some rough thoughts on building a file-system routing based web application
Published: ....
Slicing software: why vertical is better than horizontal.
Published: ....
What if you could author an entire web application in a single file?
Published: ....
A quick way to handle resetting internal state in components when a parent form is submitted!
Published: ....
A brief look at Import Maps and package.json#imports to support isomorphic JavaScript applications!
Published: ....
A collection of tech talks that I regularly re-watch and also recommend to everyone!
Published: ....
Some features and functionality that I'd like within a React Server Component compatible framework.
Published: ....
A (running) collection of Bluesky tips, tools, packages, and other misc things!
Published: ....
A quick look at a small but powerful pattern I've been leveraging as of late!
Published: ....
A proposal for a minimal variant of TypeScript!
Published: ....
Sharing a few core recommendations when working within monorepos to make your life easier!
Published: ....
This is a quick post noting that Next.js should now work with Deno v2!
Published: ....
React components have a fundamental contract that is often unstated in their implementation, and you should know about it!
Published: ....
Replace that old useState and useEffect combo for a new and better option!
Published: ....
A quick look at the applications and tools that I (generally) use day to day for web development!
Published: ....
There are a variety of different markdown "standards" out there, and sometimes they're not all that consistent
Published: ....
Proposing a solution for sharing core "business" logic across services!
Published: ....
There's a common gotcha when creating Web Request and Response instances with Headers!
Published: ....
Feature toggles are often underused by most software development teams, and yet offer so much value during not only feature development but also refactors
Published: ....
A quick introduction to my new side project, hohoro. An incremental JS/TS library build tool!
Published: ....
Two neat tricks for enhancing your site's favicon!
Published: ....
The various risks and pitfalls of open source software run by corporations.
Published: ....
A monorepo template for managing a library and documentation together.
Published: ....
How we solved an almost show-stopping production bug, and how you can avoid it in your own projects.
Published: ....
A(nother) deep dive into one of my recent side projects; tails - a plain and simple cocktail recipe app.
Published: ....
When did semver major changes become so scary?
Published: ....
Leveraging service monitors properly to improve service observability.
Published: ....
A brief recap of how Wayfair changed it's CSS approach not once but twice in the span of 5 years!
Published: ....
A deep dive into one of my recent side projects; microfibre - a minimal text posting application
Published: ....
Pair programming can be good sometimes - but not all the time
Published: ....
A few thoughts on using Suspense with GraphQL to optimize application data loading
Published: ....
A few thoughts on what to do after you launch a new project
Published: ....
A few quick thoughts on burn out and taking a break
Published: ....
A few thoughts on managing complex UI component state within React
Published: ....
A quick overview of the new lifecycle methods introduced in React 16.3
Published: ....
A few thoughts and patterns for using styled-jsx or other CSS-in-JS solutions
Published: ....
A few thoughts on the redesign of my personal site, adopting Next.js and deploying via Now
Published: ....
A few weird things about JavaScript
Published: ....
Building a calendar web application
React
Published: ....
A quick tip to implementing CSS theming that's compatible with Server Side Rendered applications!
Published: ....
A quick way to handle resetting internal state in components when a parent form is submitted!
Published: ....
Some features and functionality that I'd like within a React Server Component compatible framework.
Published: ....
React components have a fundamental contract that is often unstated in their implementation, and you should know about it!
Published: ....
Replace that old useState and useEffect combo for a new and better option!
Published: ....
Revising my previous blog post on React Error Boundaries and my preferred go-to implementation!
Published: ....
A few thoughts on using Suspense with GraphQL to optimize application data loading
Published: ....
A few thoughts on managing complex UI component state within React
Published: ....
A quick overview of the new lifecycle methods introduced in React 16.3