Error Boundaries in React

Pranay Chauhan
Last Updated: May 13, 2022

Introduction

We see that runtime errors during rendering could put our application in a broken state. React unmounts the whole react component tree.

It would be great to catch the errors anywhere in the component tree and display a fallback UI.

This is where error boundaries take the spotlight now; let us see an error boundary.

Let us learn an exciting feature in react, which is error boundaries.

If we recollect from the life cycle hooks, The error handling phase, which includes two life cycle methods that are used to form error boundaries:

  1. Static getDerivedStateFromError(error)
  2. componentDidCatch(error,info)

 

Now let us dive deep into the concept of portals in react.

What are Error Boundaries?

A class component that implements either one or both of the lifecycle methods getDerivedStateFromError or componentDidCatch becomes an error boundary. 

The static method getDerivedStateFromError is used to render a fallback UI after an error is thrown, and the componentDidCatch method is used for logging their information.

Example

Let us understand this better with an example.

Hero.js

Create a new file here, Hero.js.

Within the file, we create a functional component. This component will accept the hero name as a prop and will render the same. For our understanding of error boundaries, though, we need to throw an error, so what we will add is if the hero name is Joker, throw an error that says not a hero.

import React from "react";

 

function Hero({ heroName }) {

  if (heroName === "Joker") {

    throw new Error("Not a hero!");

  }

  return <div>{heroName}</div>;

}

 

export default Hero;

App.js

In the App component, We include two heroes the first one, heroName, is equal to Batman and the second heroName is equal to Superman.

import React from "react";

import "./App.css";

import Hero from "./components/Hero";

function App() {

  return (

    <div classname="App">

      <Hero heroName="Batman"/>

      <Hero heroName="Superman"/>

    </div>

  );

}

export default App;

 

Output:

Now, let us add another hero. We pass Joker as the heroName in the App.js file.

<Hero heroName="Joker"/>

 

Output:

If you now take a look at the browser, you can see that our entire application crashes. This is not good.

We want if a particular component throws an error, only that component should fall back into a UI, and the remaining components should be unaffected. 

Let us see how to achieve that with an error boundary.

How to implement error boundaries? 

First, to learn the implementation of error boundaries, we create a new file, ErrorBoundary.js, a class component without export keywords.

We need to define either the getDerivedStateFromError or componentDidCatch lifecycle methods.

Using getDerivedStateFromError

Let us start with the first one, static getDerivedStateFromError, and it receives the error as a parameter within the body; we are simply going to return the new state object. 

ErrorBoundary.js

What we will do is set a property called hasError to true in the getDerivedStateFromError.

And we will also add the state of hasError and initialize it to false.

In the render method, if the state variable hasError is true, we will return a heading tag of the message “something went wrong!” otherwise, we will return the children of the props.

import React, { Componentfrom "react";

class ErrorBoundary extends Component {

  constructor(props) {

    super(props);

    this.state = {

      hasErrorfalse,

    };

  }

  static getDerivedStateFromError(error) {

    return {

      hasErrortrue,

    };

  }

  render() {

    if (this.state.hasError) {

      return <h1>Something went wrong!</h1>;

    }

    return this.props.children;

  }

}

export default ErrorBoundary;

 

So what we are effectively doing is if there is an error when rendering any of the components we are setting, the state has an error property to true. This state property can now be used to create a fallback UI 

Our error boundary is now complete.

App.js

The final step is to wrap the components with this error boundary. So in App.js, we cover all the hero components with the error boundary component.

import React from "react";

import "./App.css";

import ErrorBoundary from "./components/ErrorBoundary";

import Hero from "./components/Hero";

function App() {

  return (

    <div classname="App">

      <ErrorBoundary>

        <Hero heroName="Batman" />

        <Hero heroName="Superman" />

        <Hero heroName="Joker" />

      </ErrorBoundary>

    </div>

  );

}

export default App;

 

Output

We still see the error this might be confusing, but this is the intended behavior. The react team have mentioned that error boundaries are primarily helpful for production, but in development, they want to make errors as highly visible as possible, so you will always

see this error during development. 

But we can click on the close button, and we now have our application back, and you can see that we have the text “something went wrong!” displayed. 

 

 

Without the use of error boundaries, this screen will generally be blank. So this is the difference, and our boundaries will catch the error and display a fallback UI which in our case is the text “something went wrong!” 

Where to place error boundaries?

A very important point to discuss the boundaries is where to place them. 

Right now, our error boundary encloses all the hero components, so if there is an error, everything inside the error boundary is hidden, and the fallback UI is rendered. This, however, might not be ideal. 

Example

Consider an e-commerce site where we display a thousand products. Just because there is an error in one of the products, it would not be a great idea to hide the other products. 

Similarly, in our example, it's not a good idea to hide the other two heroes when the third hero is throwing an error to wrap each hero component with the error boundary in the App component.

App.js

import React from "react";

import "./App.css";

import ErrorBoundary from "./components/ErrorBoundary";

import Hero from "./components/Hero";

function App() {

  return (

    <div classname="App">

      <ErrorBoundary>

        <Hero heroName="Batman" />

      </ErrorBoundary>

      <ErrorBoundary>

        <Hero heroName="Superman" />

      </ErrorBoundary>

      <ErrorBoundary>

        <Hero heroName="Joker" />

      </ErrorBoundary>

    </div>

  );

}

export default App;

 

Ideally, you would want this as a reusable component, but this will do for now. If I go back to the browser, you can see that we still have the error overlay. When we close the error overlay, you can see that the first two heroes are displayed, and only the third hero has the fallback UI.

Output:

We click on the top right corner close button, and we see

 

 

The placement of the error boundary is entirely up to you. You can just wrap the top-level component or wrap any nested individual components so that only that component has a fallback UI, leaving the rest of your user interface works as expected.

Using componentDidCatch 

Let us get to the second one componentdidCatch right after getDerivedStateFromError.

We have componentDidCatch, which takes two parameters error and info, the information related to the error. This method is used for logging the errors, so if you have a logging service, you can call it passing in error and the info parameters. 

For now, we will simply log them to the console. This will seem redundant, though, because react also logs the error into the console by default.

ErrorBoundary.js

import React, { Componentfrom "react";

class ErrorBoundary extends Component {

  constructor(props) {

    super(props);

    this.state = {

      hasErrorfalse,

    };

  }

  static getDerivedStateFromError(error) {

    return {

      hasErrortrue,

    };

  }

  componentDidCatch(errorinfo) {

    console.log(error);

    console.log(info);

  }

  render() {

    if (this.state.hasError) {

      return <h1>Something went wrong!</h1>;

    }

    return this.props.children;

  }

}

export default ErrorBoundary;

 

Output in console

 

The final point is that you should know that our boundaries catch errors during rendering in lifecycle methods and in the constructors of the whole tree below them however they do not catch errors inside event handlers.

If you have an onclick handler and want to catch an error you just need to use the regular try-catch statements and not error boundaries. 

Alright, that is pretty much about error boundaries 

Frequently Asked Questions (FAQs)

What are error boundaries in react?

Error boundaries are React components that catch JavaScript errors in their child component tree, log those errors and display a fallback UI.

 

Why are error boundaries in react required?

Error boundaries in react basically provide a way to handle errors in application code gracefully.

 

What are the two methods used for error boundary implementation?

The error handling phase, which includes two life cycle methods

  1. Static getDerivedStateFromError(error)
  2. componentDidCatch(error,info)

Key Takeaways

We learned about the concept of error boundaries in react. This article explained what error boundaries in react are specifically. We also learned about the implementation of error boundaries in react.

Apart from this, you can also expand your knowledge by referring to these articles on Javascript and React.

For more information about the react framework for frontend development, get into the entire Frontend web development course.

 

Was this article helpful ?
0 upvotes

Comments

No comments yet

Be the first to share what you think