Categories
JavaScript React

Create React Error Boundary Components to Handle Errors Gracefully

Spread the love

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

We can handle the error gracefully with error boundary components.

In this article, we’ll look at how to define and use them.

Error Boundaries

JavaScript inside components used to corrupt React’s internal state and cause cryptic errors to be emitted on the next renders.

These errors are always caused by earlier errors in the application code. React didn’t provide a way to handle them gracefully in components and recover from them.

We can use error boundaries to catch JavaScript errors anywhere in the child component tree, log those errors and display a fallback UI instead of crashing the app.

However, error boundaries don’t catch errors for event handlers, asynchronous code, server-side rendering, or errors thrown in the error boundary itself.

We can create an error boundary component by adding the static getDerivedStateFromError() or componentDidCatch() methods inside a class-based component.

For example, we can use it as follows:

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

  static getDerivedStateFromError(error) {  
    return { hasError: true };  
  } 

  componentDidCatch(error, errorInfo) {  
    console.log(error, errorInfo);  
  } 

  render() {  
    if (this.state.hasError) {  
      return <h1>Error occured.</h1>;  
    } return this.props.children;  
  }  
}

class Button extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = { count: 0 };  
  }  
  increment() {  
    this.setState({ count: this.state.count + 1 });  
  }  
  render() {  
    if (this.state.count === 5) {  
      throw new Error("error");  
    } 

    return (  
      <>  
        <button onClick={this.increment.bind(this)}>Click Me</button>  
        <br />  
        <p>{this.state.count}</p>  
      </>  
    );  
  }  
}

class App extends React.Component {  
  render() {  
    return (  
      <div>  
        <ErrorBoundary>  
          <Button />  
        </ErrorBoundary>  
      </div>  
    );  
  }  
}

In the code above, we have the ErrorBoundary component, which has the componentDidCatch method to log the errors given by the parameters.

Then we added the static getDerivedStateFromError to set the hasError state to true if an error occurred.

In the render method, we render the error message if hasError state is true . Otherwise, we display the child components as usual.

In the Button component, we have a button element that increases the count state it’s clicked. We have an if statement for this.state.count which throws an error if it reaches 5.

Once it reaches 5, the ‘Error occurred’ message from the ErrorBoundary component will be shown since the error is thrown in the render method, which isn’t an event handler, async code, and other places error boundaries can’t catch.

As we can see, we placed the ErrorBoundary outside any code that catches errors. This way, ErrorBoundary can actually catch them.

During development, the whole stack trace will be displayed, we have to disable that in production. This will be done automatically when we make a production build with Create React App.

Catching Errors Inside Event Handlers

We have to use try/catch block to catch errors in event handlers.

For example, we can write the following code to catch those:

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = { error: null, count: 0 };  
    this.handleClick = this.handleClick.bind(this);  
  } 

  handleClick() {  
    try {  
      if (this.state.count === 5) {  
        throw new Error("error");  
      }  
      this.setState({ count: this.state.count + 1 });  
    } catch (error) {  
      this.setState({ error });  
    }  
  } 
  
  render() {  
    if (this.state.error) {  
      return <h1>Error occurred.</h1>;  
    }  
    return (  
      <button onClick={this.handleClick}>Count: {this.state.count}</button>  
    );  
  }  
}

In the handleClick method, we have an if statement that throws an error if this.state.count is 5.

If an error is thrown, the catch block will set the error state to the error object.

This will then render the ‘Error occurred’ message instead of the button to increase the count state.

Conclusion

We can use error boundary components to handle errors gracefully and log them.

To create an error boundary component, we add a static getDerivedStateFromError() or componentDidCatch() method to do that.

We use componentDidCatch to log errors and getDerivedStateFromError to render a fallback UI.

We wrap the error component around child components that may throw errors.

Error boundary can catch errors in places other than async code, event handlers, server-side rendering code, or errors thrown in the error boundary itself.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

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