Tutorial

A Simple Guide to Error Boundaries in React

React

While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

In this article, you’ll learn about Error Boundaries via code snippets and interactive demos. If you’re still wondering what Error Boundaries are and how they work, this simple guide is for you! ๐Ÿฅ…๐Ÿ›

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! ๐Ÿ˜ฑ

Without Error Boundaries

It’s inevitable that we’ll encounter unexpected errors in our apps. You could be trying to access a deeply-nested property on an object that doesn’t exist, or sometimes it’s not in your control (like a failed HTTP request to a 3rd-party API).

In the demo below, we’ll simulate an error to see what normally happens without an Error Boundary.

Click the “+” button, and it will fail at 5:

See the Pen alligatorio-react-error-boundaries-1 by wle8300 (@wle8300) on CodePen.

BuggyCounter.js
class BuggyCounter extends Component {
  state = {
    counter: 0
  };
  handleClick = () => {
    this.setState({
      counter: this.state.counter + 1
    });
  }
  render() {
    if (this.state.counter === 5) {
  // Simulate an error!
  throw new Error('I crashed!');
}
    return (
      <div>
        <h1>{this.state.counter}</h1>
        <button onClick={this.handleClick}>+</button>
      </div>
    );
  }
};

When the app encounters an error, the component completely unmounts itself and the user is left with a blank HTML page ๐Ÿ‘ป. This can obviously leave users feeling confused, and they won’t know what to do next.

Error Boundaries provides a way to gracefully handle these errors!

With Error Boundaries

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’s the usage of 2 methods that are available in React components:

MyErrorBoundary.js
class MyErrorBoundary extends 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
  logErrorToMyService(error, info);
}

  render() {
    if (this.state.error) {
      // You can render any custom fallback UI
      return <p>Something broke</p>;
    }
    return this.props.children; 
  }
};

What are these 2 lifecycle methods?

  • 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 (eg.: this.props.children).
  • componentDidCatch is a lifecycle method designed for triggering side-effects (eg.: 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.

So what is an Error Boundary? Any React Component is considered an Error Boundary when it employs at least one of these lifecycle methods.


Good practices suggest that you’ll want to create a component that’s 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’ll catch the error!

class MyErrorBoundary extends 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
  }
}

class BuggyCounter extends Component {
  // nothing was modified :P
}

function App() {
  refreshPage = () => history.go(0)
  return (
    <div>
      <MyErrorBoundary>
  <BuggyCounter />
</MyErrorBoundary>
      <hr />
      <button onClick={this.refreshPage}>
        Refresh Page
      </button>
    </div>
  );
}

Try clicking the “+” button again. Once it reaches 5, it will crash gracefully. Additionally, you can open your console to view a stack trace!

See the Pen alligatorio-react-error-boundaries-2 by wle8300 (@wle8300) on CodePen.


Instead of completed 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’s a great way to redeem an otherwise unfortunate user experience.

Error Boundaries are only available in Class-based React Components. At the time of this writing, there isn’t a way to implement it using React Hooks yet.

Error Boundaries vs 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:

  • During render phase
  • In a lifecycle method
  • In the constructor

Basically… the React-y parts of a component. As a counterpoint, these are the places where Error Boundaries won’t be able to catch an error:

  • Event handlers (eg., onClick, onChange, etc.)
  • setTimeout or requestAnimationFramecallbacks
  • Server-side rendering (SSR)
  • And errors causesd by the error boundary itself (rather than its children)

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 โš”๐Ÿ›ก

Conclusion

Now that Error Boundaries are available since React v16, it’s generally advisable to use at least 1 Error Boundary at the root of your app (eg., the App.js file). This will prevent users from seeing a blank HTML page, and perhaps see a nice fallback UI instead.

If you want to go the extra mile you can employ several different kinds of Error Boundaries that employ different fallback UIs, or those that only log errors to a 3rd-party service.

Check out the official React docs for more info! ๐Ÿ“

0 Comments

Creative Commons License