<< All Blog Posts
Handling Errors with Error Boundaries in React

Handling Errors with Error Boundaries in React

error1.png

Error boundaries were introduced in React 16 as a way to catch JavaScript errors in your React components.

Error boundaries are React components that catch errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.

error2.png

However, error boundaries do not handle errors in:

  • event handlers,
  • asynchronous code (e.g., setTimeout or requestAnimationFrame callbacks),
  • server-side rendering; or
  • the error boundary itself (rather than its children).

So basically, error boundaries only handle errors in the parts of our code that involve React.

A simple error boundary component would look like this:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

Then we can use this component anywhere in our app:

<ErrorBoundary>
  <App />
</ErrorBoundary>

The key point in the previous example is the getDerivedStateFromError. This lifecycle method is invoked after an error has been thrown by a descendant component. It receives the error that was thrown as a parameter and should return a value to update state.

So when any component inside the app returns an error, error boundary’s getDerivedStateFromError will trigger and set the hasError state to true. The render method will then render the fallback UI instead of the children.

Using ErrorBoundary with issue tracking tools

If you are using issue tracking tools in your project, ErrorBoundary is all you need via the componentDidCatch lifecycle method. This lifecycle is invoked after an error has been thrown by a descendant component. It receives two parameters:

  • error: The error that was thrown.
  • info: An object with a componentStack key containing information about which component threw the error.

So let’s see it in action. Adding componentDidCatch to our ErrorBoundary:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }
  componentDidCatch(error, info) {
    Sentry.withScope((scope) => {
      scope.setContext(..., {
        error,
        info,
      });
      Sentry.captureException(error);
    });
  }
  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

You may think that setting state can be done in componentDidCatch as well but you shouldn’t. Here is a note from official docs about why:

“In the event of an error, you can render a fallback UI with componentDidCatch() by calling setState, but this will be deprecated in a future release. Use static getDerivedStateFromError() to handle fallback rendering instead.”

So with this final version of our ErrorBoundary we are able to catch any error in the application and report them to the issue tracking tool if we put our ErrorBoundary component to root level of the app.

When developing React applications, a page is typically composed of several components in a tree that can escalate quickly. For that reason, if there’s an error in the application, it’s commonly lost in the hierarchy of components.

Error boundaries can help you by raising the red flag on the exact node that experienced the flaw. Here is the official demo of how it will work if we use error boundaries with each single component.

Bonus: Use TypeScript!

TypeScript adds additional syntax to JavaScript to support a tighter integration with your editor so you can catch errors early. If used well, TypeScript can help you eliminate any errors in the app.

Having said that, it doesn’t mean that you don’t need to have an ErrorBoundary. It is still possible to miss things with TypeScript, especially when you generate manually or get an unexpected response from the backend that may cause a page break even if you used TypeScript as expected. So in that case, it is better to show your custom fallback UI to the user instead of a blank page!

2nd Bonus: Sentry Error Boundary

If you are using Sentry, it already has its own ErrorBoundary component. You may want to use it or check if your tracking tool has one. Regardless of how you do it, ErrorBoundary has your back.


Thanks for filling out the form! A Six Feet Up representative will be in contact with you soon.

Connect with us