Learnitweb

Error boundary in React functional component

React does not allow you to catch render-time errors inside functional components without using a class-based component. This is because:

Only class components can implement componentDidCatch and getDerivedStateFromError, which are required to catch rendering errors in React.

Why You Can’t Build a Purely Functional Error Boundary Today

React’s error boundary mechanism is built around lifecycle methods that only exist in class components. As of React 18 and even in experimental React 19, functional components cannot implement componentDidCatch.

Here’s a summary:

FeatureFunctional ComponentsClass Components
componentDidCatch()Not supportedYes
getDerivedStateFromError()Not supportedYes
Can catch render errorsNoYes

What Can You Do Functionally?

While you can’t catch render-time errors functionally, you can catch event-based or effect-based errors using:

  1. try/catch inside event handlers
  2. Error management inside useEffect
  3. ErrorBoundary wrapper around functional children

Step 1: Create a Class-based ErrorBoundary

import React from 'react';

export class MyErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

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

  componentDidCatch(error, errorInfo) {
    if (this.props.onError) {
      this.props.onError(error, errorInfo);
    }
  }

  reset = () => {
    this.setState({ hasError: false, error: null });
  };

  render() {
    if (this.state.hasError) {
      const Fallback = this.props.fallback;
      return (
        <Fallback
          error={this.state.error}
          resetErrorBoundary={this.reset}
        />
      );
    }

    return this.props.children;
  }
}

Step 2: Create a Fallback UI Component

import React from 'react';

function FallbackUI({ error, resetErrorBoundary }) {
  return (
    <div style={{ padding: 20, border: '1px solid red', backgroundColor: '#fce4e4' }}>
      <h2>Oops! Something went wrong.</h2>
      <p style={{ color: 'red' }}>{error.message}</p>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

export default FallbackUI;

Step 3: Create a Custom Hook and Wrapper for Functional Use

import { useCallback, useRef } from 'react';
import { MyErrorBoundary } from './MyErrorBoundary';

/**
 * This hook is for functional components that want to use imperative error handling.
 */
export function useCustomErrorBoundary() {
  const ref = useRef();

  const reset = useCallback(() => {
    ref.current?.reset();
  }, []);

  return {
    ErrorBoundary: ({ children, fallback, onError }) => (
      <MyErrorBoundary
        ref={ref}
        fallback={fallback}
        onError={onError}
      >
        {children}
      </MyErrorBoundary>
    ),
    reset,
  };
}

Step 4: Use It in a Functional Component

import React from 'react';

function BuggyComponent({ shouldCrash }) {
  if (shouldCrash) {
    throw new Error('BuggyComponent crashed!');
  }

  return <p>This component is working fine.</p>;
}

export default BuggyComponent;