7 min read

React Error Boundaries: Saving Your App from Crashing

Introduction: Errors Happen (It’s Okay!)

Even the most experienced developers make mistakes. In the world of React, errors in components can lead to a crashed application and a frustrating user experience. But fear not! React provides a powerful tool called Error Boundaries to catch these errors, prevent crashes, and display a fallback UI. This post will guide you through everything you need to know about Error Boundaries, with clear explanations, helpful code examples in TypeScript, real-world case studies, and an introduction to the react-error-boundary library.

What are Error Boundaries? Your App’s Safety Net

Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the crashed component tree. They’re like a safety net for your application, preventing a single error from bringing down the entire show.

How Error Boundaries Work: Catching Errors Like a Pro

Error boundaries work by implementing one or both of the following lifecycle methods:

  • static getDerivedStateFromError(error): This method is called after an error has been thrown by a descendant component. It receives the error as an argument and should return an object to update the state, typically to display a fallback UI.
  • componentDidCatch(error, errorInfo): This method is called after an error has been thrown by a descendant component. It receives two arguments: the error and information about the error (including a stack trace). This method is typically used for logging the error to an error tracking service.

Important Note: Error boundaries only catch errors in the components below them in the tree. They do not catch errors in:

  • The error boundary itself.
  • Event handlers.
  • Asynchronous code (e.g., setTimeout or requestAnimationFrame callbacks).
  • Server-side rendering.

Creating an Error Boundary: TypeScript Example

Here’s a step-by-step guide to creating an Error Boundary in TypeScript.

import React, { Component, ReactNode } from "react";

interface ErrorBoundaryState {
  hasError: boolean;
  error?: Error;
  errorInfo?: React.ErrorInfo;
}

class ErrorBoundary extends Component<{}, ErrorBoundaryState> {
  constructor(props: {}) {
    super(props);
    this.state = { hasError: false, error: undefined, errorInfo: undefined };
  }

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

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    // Log the error to an error reporting service
    console.error("Caught an error:", error, errorInfo);

    // You can also log the error to your favorite error tracking service here, e.g., Sentry, LogRocket
    this.setState({ error, errorInfo });
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return (
        <div>
          <h1>Something went wrong.</h1>
          <p>We're working on it. Please try again later.</p>
          <details style={{ whiteSpace: "pre-wrap" }}>
            <summary>Error Details</summary>
            {this.state.error?.message}
            <br />
            {this.state.errorInfo?.componentStack}
          </details>
        </div>
      );
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

Using the Error Boundary: Protecting Your Components

Wrap the component(s) you want to protect with the ErrorBoundary component:

import React from "react";
import ErrorBoundary from "./ErrorBoundary";
import MyComponent from "./MyComponent"; // This might be a component prone to errors

function App() {
  return (
    <div>
      <ErrorBoundary>
        <MyComponent />
      </ErrorBoundary>
      {/* Other components outside the error boundary will not be affected by errors in MyComponent */}
    </div>
  );
}

Case Studies: Real-World Examples

E-commerce Product Page

Imagine a product page that crashes due to an error in fetching product data. An Error Boundary could catch the error and display a user-friendly message like, “Oops! Something went wrong. We’re working on it,” along with a retry button. This prevents a blank screen and provides a much better user experience.

Social Media Feed

If an error occurs while rendering a post in a social media feed, an Error Boundary can catch the error and prevent the entire feed from crashing. It could display a placeholder for the problematic post, allowing users to continue browsing the rest of their feed.

Data Visualization Dashboard

In a complex data visualization dashboard, an error in one chart shouldn’t bring down the entire dashboard. An Error Boundary can isolate the faulty chart and display an error message, ensuring that the other charts remain functional.

Best Practices: Error Boundary Wisdom

  • Use Multiple Error Boundaries: Wrap different parts of your application in separate Error Boundaries to isolate errors and prevent cascading failures. Don’t just put one at the root of your app!
  • Provide Informative Feedback: Don’t just say “Something went wrong.” Provide specific error messages (when appropriate) and guidance on what the user can do (e.g., reload the page, contact support).
  • Log Errors: Use componentDidCatch to log error details to a service like Sentry or LogRocket. This helps you track and fix errors more effectively.
  • Don’t Overuse Error Boundaries: Error boundaries shouldn’t replace proper error handling and defensive coding. Use them strategically to handle unexpected errors and prevent crashes.
  • Consider the User Experience: Your fallback UI should be user-friendly and informative. A well-designed fallback UI can turn a potentially frustrating experience into a positive one.

Introduction to react-error-boundary Library

The react-error-boundary library simplifies the process of adding error boundaries to your React applications. It provides a convenient wrapper component and additional features that make it easier to handle errors gracefully.

Installing react-error-boundary

You can install the react-error-boundary library using npm or yarn:

npm install react-error-boundary

or

yarn add react-error-boundary

Using react-error-boundary in TypeScript

Here’s how you can use the react-error-boundary library in a TypeScript project:

import React from "react";
import { ErrorBoundary } from "react-error-boundary";
import MyComponent from "./MyComponent"; // This might be a component prone to errors

function FallbackComponent({
  error,
  resetErrorBoundary,
}: {
  error: Error;
  resetErrorBoundary: () => void;
}) {
  return (
    <div>
      <h1>Oops, something went wrong!</h1>
      <p>{error.message}</p>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

function App() {
  return (
    <div>
      <ErrorBoundary
        FallbackComponent={FallbackComponent}
        onReset={() => {
          // Reset the state of your app here
        }}
      >
        <MyComponent />
      </ErrorBoundary>
      {/* Other components outside the error boundary will not be affected by errors in MyComponent */}
    </div>
  );
}

export default App;

Key Features of react-error-boundary

  • FallbackComponent: Allows you to specify a component to render when an error occurs.
  • onReset: A callback function called when the user attempts to reset the app state after an error.
  • FallbackRender: A render prop alternative to FallbackComponent.

Example with FallbackRender

import React from "react";
import { ErrorBoundary } from "react-error-boundary";
import MyComponent from "./MyComponent"; // This might be a component prone to errors

function App() {
  return (
    <div>
      <ErrorBoundary
        fallbackRender={({ error, resetErrorBoundary }) => (
          <div>
            <h1>Oops, something went wrong!</h1>
            <p>{error.message}</p>
            <button onClick={resetErrorBoundary}>Try again</button>
          </div>
        )}
        onReset={() => {
          // Reset the state of your app here
        }}
      >
        <MyComponent />
      </ErrorBoundary>
      {/* Other components outside the error boundary will not be affected by errors in MyComponent */}
    </div>
  );
}

export default App;

Conclusion: Embrace the Errors (and Handle Them Gracefully)

Errors are inevitable in software development. But with React Error Boundaries, you can handle them gracefully, prevent crashes, and provide a much better user experience. Use this powerful tool wisely, and your React apps will be more robust, reliable, and user-friendly. No more tears (at least not from crashed apps)!

References: