Mastering Error Boundaries in React: A Guide to Effective Error Handling

dev_diaries_by_varun

Varun Kelkar

Posted on July 19, 2024

Mastering Error Boundaries in React: A Guide to Effective Error Handling

What is an Error Boundary?

While building applications, errors are inevitable. They might come from APIs, UI or from several other places.

It's very important to handle these errors gracefully & maintain good UX despite these errors.

Error Boundary is one such way of error handling in React.


How does Error Boundary help?

To understand this, let's understand the situation before the introduction of the Error Boundary.

Before Error Boundaries, the errors occurring inside components eventually propagated & broke the UI or rendered the white screen.

This caused a really bad UX.

Error Boundary helps us to handle such errors & display a fallback UI instead of breaking the UI or white screen displayed.


How to use Error Boundary?

React v16 officially introduced Error Boundary.

It's a Class-Based Component which can be used to wrap your application.

It accepts a fallback UI to be displayed in case your application has errors or otherwise, it simply renders the children component to resume the normal flow of your application.

This is how the React Documentation recommends using it,

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

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // Example "componentStack":
    //   in ComponentThatThrows (created by App)
    //   in ErrorBoundary (created by App)
    //   in div (created by App)
    //   in App
    logErrorToMyService(error, info.componentStack);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return this.props.fallback;
    }

    return this.props.children;
  }
}

Enter fullscreen mode Exit fullscreen mode

What's the problem with React's Error Boundary?

It cannot catch errors occurring in,

  • Event Handlers (these errors need to be handled with try-catch blocks)
  • Asynchronous Code (APIs, setTimeout, requestAnimationFrame etc.)
  • Server-side rendering
  • The error that occurs in Error Boundary itself
  • It does not work with Functional Components. Although we can make it work with a few code changes.
  • Hooks cannot be used inside it.

What's the solution?

There's a npm package called react-error-boundary which is a wrapper on top of the traditional Error Boundary component.

Using this package, we're able to overcome all the issues faced in the traditional Error Boundary component.


How to use it?

You can wrap your entire application with or you can wrap individual components with .

The granularity of implementation is up to you.

Let's understand how to use it.

import React from 'react';
import { ErrorBoundary } from "react-error-boundary";

const App = () => {
return <ErrorBoundary fallback={<div>Something went wrong</div>}>
/* rest of your component */
</ErrorBoundary>
}

Enter fullscreen mode Exit fullscreen mode

This is the simplest example of using ErrorBoundary. There's more to this library.


Understanding react-error-boundary API

Let's try to understand API with different scenarios.

1. I want to show a generic fallback UI for Errors in the Application

 import React from 'react';
 import { ErrorBoundary } from "react-error-boundary";

 const App = () => {
 return <ErrorBoundary fallback={<div>Something went wrong</div>}>
 /* rest of your component */
 </ErrorBoundary>
 }
Enter fullscreen mode Exit fullscreen mode

2. I want to show specific error details in my fallback component

 import React from 'react';
 import { ErrorBoundary } from "react-error-boundary";

 function fallbackRender({ error, resetErrorBoundary }) {
   // Call resetErrorBoundary() to reset the error boundary and retry the render.
   return (
     <div role="alert">
       <p>Something went wrong:</p>
       <pre style={{ color: "red" }}>{error.message}</pre>
     </div>
   );
 }

 const App = () => {
 return <ErrorBoundary 
 fallbackRender={fallbackRender}
 onReset={(details) => {
     // Reset the state of your app so the error doesn't happen again
   }}
 >
 /* rest of your component */
 </ErrorBoundary>
 }

Enter fullscreen mode Exit fullscreen mode

Instead of fallback or fallbackRender, you can also use a React Component.

 import React from 'react';
 import { ErrorBoundary } from "react-error-boundary";

 const Fallback = ({ error, resetErrorBoundary }) => {
   // Call resetErrorBoundary() to reset the error boundary and retry the render.
   return (
     <div role="alert">
       <p>Something went wrong:</p>
       <pre style={{ color: "red" }}>{error.message}</pre>
     </div>
   );
 }

 const App = () => {
 return <ErrorBoundary 
 FallbackComponent={Fallback}
 onReset={(details) => {
     // Reset the state of your app so the error doesn't happen again
   }}
 >
 /* rest of your component */
 </ErrorBoundary>
 }
Enter fullscreen mode Exit fullscreen mode

3. I want to Log my errors

 import React from 'react';
 import { ErrorBoundary } from "react-error-boundary";

 const logError = (error: Error, info: { componentStack: string }) => {
   // Do something with the error, e.g. log to an external API
 };

 const Fallback = ({ error, resetErrorBoundary }) => {
   // Call resetErrorBoundary() to reset the error boundary and retry the render.
   return (
     <div role="alert">
       <p>Something went wrong:</p>
       <pre style={{ color: "red" }}>{error.message}</pre>
     </div>
   );
 }
 // You can use fallback / fallbackRender / FallbackComponent anything
 const App = () => {
 return <ErrorBoundary 
 FallbackComponent={Fallback}
 onError={logError}
 onReset={(details) => {
     // Reset the state of your app so the error doesn't happen again
   }}
 >
 /* rest of your component */
 </ErrorBoundary>
 }

Enter fullscreen mode Exit fullscreen mode

4. I want to catch errors in event handlers & async code

 import { useErrorBoundary } from "react-error-boundary";

 function Example() {
   const { showBoundary } = useErrorBoundary();
   const getGreeting = async(name) => {
     try {
         const response = await fetchGreeting(name);
         // rest of your code
     } catch(error){
          // Show error boundary
         showBoundary(error);
     }
   }
   useEffect(() => {
    getGreeting()
   });

   return <Whatever UI you want to render/>
 }

Enter fullscreen mode Exit fullscreen mode

A few gotchas

ErrorBoundary is a client component. You can only pass props to it that are serializeable or use it in files that have a "use client"; directive.

1. What is a serializable prop?

Serilzable prop means it can be converted to byte stream in such a way that byte stream can be converted back to orignal prop.

A common way to do that in Javascript is JSON.stringify() & JSON.parse().

2. How to use "use client"; directive?

Simply mention it at top of the file

"use client";

Enter fullscreen mode Exit fullscreen mode

There are a few more variations you can use. But this article is good enough to get you started.

Checkout their full documentation here.

Please let me know in the comments if you found it helpful.

Happy Coding!

πŸ’– πŸ’ͺ πŸ™… 🚩
dev_diaries_by_varun
Varun Kelkar

Posted on July 19, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related