React 16: What’s new? Error handling!

Ben Gourley in Engineering on September 26th, 2017

React shipped a major version (v16.0) this week. Because the project is developed in the open, the community has been able to track the progress of this release, get a feel for what’s on the horizon, and even shape what lands in it. Naturally, we’ve been eagerly anticipating the overhaul of error handling.

Here we’ll summarise what’s new, dive into error handling and show you how to make the most of the new features by hooking them up to Bugsnag.

What’s changed?

From the outside: in short, not a huge amount. However, the internals have had a complete rewrite – a project known as “Fiber”, which has been underway for over a year.

Because of this huge internal changeset, the maintainers focused largely on compatibility with existing apps – wisely enabling them to leverage the huge ecosystem of working React apps in the wild as a catalogue of real-life test cases. Luckily for you, some ahead-of-the-curve teams tried out the release candidates and helped to iron out the creases. 😉

As part of the rewrite many private/internal APIs have changed or been removed, so if you were monkeypatching or fiddling with anything you shouldn’t have, you will probably need to update that.

The main feature additions are:

You can read all about the custom DOM attributes and render() return types on the React blog – they’re nice, but we’re much more interested in error handling (surprised, much?).

Handling errors in React

Handling errors in React has been a frustrating endeavour. There’s been no consistent and failsafe way to catch and deal with errors, until now! The Fiber rewrite laid the groundwork for better error handling, starting with a new feature called Error Boundaries.

An error boundary is a React component with a componentDidCatch(err, info) method. Any errors occurring in a component tree get reported up to the nearest error boundary’s componentDidCatch function, giving your app the chance to:

  • Handle the error gracefully (e.g. call setState(…) to render an error/fallback state).
  • Send the error to a reporting service, such as Bugsnag (more on this in just a sec!).

Additionally, unhandled errors (those not caught by an error boundary) will cause the entire tree to be unmounted. Quite a drastic measure indeed, but considering the alternative of attempting to continue with the app in some undefined state… that’s what got us into this mess in the first place!

The upshot is that you should use error boundaries in your app so that when something goes wrong, you provide your users with a sensible UI, and yourselves with a detailed report of what happened.

Sending React errors to Bugsnag

We’d find it hard to disagree with this advice from the React team when they introduced Error Boundaries to the world…

We … encourage you to use JS error reporting services … so that you can learn about unhandled exceptions as they happen in production, and fix them.

Bugsnag helps you do exactly that. We provide error reporting for browser JavaScript (amongst over 20 different platforms). You can sign up and start sending errors using our browser integration guide in a matter of minutes.

Our JavaScript notifier reports uncaught exceptions by default, which means if your tree gets unmounted you will already get notified with no extra effort. However, if you purposefully catch errors with error boundaries, you can report them with more contextual information which will help to you find and fix issues quicker. The simplest way to do this is to define an <ErrorBoundary /> component, as follows:

class ErrorBoundary extends React.Component {

  componentDidCatch (error, info) {
    // report the handled error to Bugsnag, and send along the "info"
    // argument which will populate the "React" tab in the event dashboard
    Bugsnag.notifyException(error, { react: info })
    // Indicate that an error happened so that you can render
    // something appropriate for your users
    this.setState({ hasError: true })
  }

  render () {
    return this.state.hasError
      ? errorMessage /* <-- some error html here */
      : this.props.children
  }

}

Then wrap your entire component tree inside the <ErrorBoundary />:

ReactDOM.render(
  <ErrorBoundary>
    <App />
  </ErrorBoundary>,
  document.getElementById('root')
)

Any component lifecycle errors within the <ErrorBoundary /> will be caught and handled by the componentDidCatch() method. It’ll be reported to your dashboard like so:

Example React error in the Busgnag dashboard

If you were wondering why we didn’t just define componentDidCatch() on our <App /> component, note that an error boundary cannot handle its own errors, only those occurring within its subtree.

To explore this in more detail, check out our example project.


So hopefully you’re as excited as we are about finally being able to handle errors gracefully in React. We wish you a smooth and speedy upgrade to v16.0 and hope to see you sending some high quality reports to your dashboard in the near future! 🎉

🚀 Bugsnag is hiring! · https://www.bugsnag.com/jobs/