React 16 introduces a new concept called Error Boundaries which aims to provide proper error handling in the event of an error in a part of the App’s UI.

You might have seen something like this:

Typical error in React

Ideally, when an error occurs during rendering, we would want to catch it and display a fallback UI instead of the regular error page. And Error Boundaries can help achieve it.

Defining an Error Boundary

As of this writing, an Error Boundary can only be created using class components.

In order for a component to become an Error Boundary, it must contain either (or both) of the following lifecycle methods:

  1. static getDerivedStateFromError()
  2. componentDidCatch()

The former allows us to render a fallback UI while the latter allows us to log the error information (the info can then be sent to a third-party service like Sentry).

class ErrorBoundary() {
  state = {
    hasError: false,
  }

  static getDerivedStateFromError(error) {
    //set hasError to true if there's an error
    return {
      hasError: true,
    }
  }

  componentDidCatch(error, info) {
    // Send the componentStack info to an external logging service   
    sendErrorToService(info.componentStack);
  }

  render() {
   if (this.state.hasError) {
     // Display a fallbackUI in the event of an error
     return <p>This is a human-friendly error message - Oops, something went wrong!</p>
   }
   
   // Otherwise, display default UI 
   return this.props.children;
  }
}

And to use it, simply wrap it around top level components like so:

<ErrorBoundary>
   <Navigation />
   <SideBar />
</ErrorBoundary>

If an error is thrown in either the <Navigation /> or <Sidebar/> component, the fallback UI will be displayed. However, in certain cases, this might not be ideal because if the error happens within a component, other components shouldn’t be affected.

To prevent that from happening, we can choose to be more granular and wrap the Error Boundary around individual components instead:

<ErrorBoundary>
  <Navigation />
</ErrorBoundary>

<ErrorBoundary>
  <SideBar />
</ErrorBoundary>

Use case

Let’s look at a simple example of when Error Boundaries can be useful. Using the <ErrorBoundary /> component above, imagine we have a Friend list application that takes in an array of objects, converts the name property to uppercase (for the fun of it!) and displays them.

import React from 'react'

const friends = [
  {
    name: 'Edmund',
    online: true,
  },
  {
    name: 'Jill',
    online: false
  },
  {
    name: "Mary",
    online: true,
  },
  {
    // Intentionally left out the name property to mimic unpredictable response from Server
    online: false,
  }
];


// FriendRow component
function FriendRow({friend}) {
  return <p>{friend.name.toUpperCase()}</p>
}

// FriendList component
function FriendList() {
  const list = friends.map(friend => {
    return (
      <ErrorBoundary>
        <FriendRow friend={friend} />
      </ErrorBoundary>
    )
  })

  return (
    <div>
      <h1>Friend list</h1>
      {list}
    </div>
  )
}

// App component
function App() {
  return(
    <div id="App">
      <FriendList />
    </div>
  )
}

Let’s assume the friends array is some JSON data we receive from a server (which we do not have control of) and that the data model isn’t always what we expect (hence the missing “name” property in the last object). Due to the missing name property, the application will throw the following error:

Missing property error

But because we wrap our <FriendRow /> component in an Error Boundary, a human-friendly error message is displayed instead:

React Error Boundary Fallback UI
Fallback UI instead of default error

Also, since our Error Boundary is wrapping individual <FriendRow /> , the error doesn’t cause the other valid components to break!

Conclusion

Error Boundaries, just like regular try/catch is useful for handling unpredictable error exceptions. Instead of a scary wall of red messages, we can now display something friendly/cool and improve our application’s overall UX.

Don’t forget to handle errors gracefully!

Leave a Reply

Your email address will not be published. Required fields are marked *