React Error Boundary Mistake

11 months ago
|
0 views

TL;DR: React's ErrorBoundary only catches errors that occur during the rendering phase—not errors that occur in event handlers. Use the react-error-boundary library to handle errors in event handlers.

When I read about React's error boundaries, I thought I could simply wrap my component with an ErrorBoundary and every JavaScript error would be caught and handled. It made sense, right?

But that's not how it works. Let's go through a common mistake and learn how to use the ErrorBoundary component properly.

React's ErrorBoundary

Let's say you already have a standard ErrorBoundary component like this:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
 
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
 
  componentDidCatch(error, errorInfo) {
    console.error(error, errorInfo);
  }
 
  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
 
    return this.props.children;
  }
}

And then you use it to wrap your component like this:

import ErrorBoundary from './ErrorBoundary';
 
const ErrorComponent = () => {
  const [message, setMessage] = useState('Hello world');
 
  const onClick = () => {
    try {
      // You would expect that this error will be caught by the ErrorBoundary, right?
      throw new Error('Some error');
    } catch (error) {
      console.error(error);
    }
  };
 
  return (
    <ErrorBoundary>
      <h1>{message}</h1>
      <button onClick={onClick}>Click me</button>
    </ErrorBoundary>
  );
};

You would expect that when you click the button, the error would be caught by the ErrorBoundary and the fallback UI would render, right?

But that's not how it works.

The ErrorBoundary only catches errors that occur during the rendering phase—not errors that occur in event handlers such as the onClick event in the example above.

The Legacy React Error Boundaries docs clearly state that:

Error boundaries do not catch errors for:

  • Event handlers (learn more)
  • Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
  • Server side rendering
  • Errors thrown in the error boundary itself (rather than its children)

In the example above, ErrorBoundary only catches errors if they occur during the rendering phase—for example, in the setMessage function:

const ErrorComponent = () => {
  const [message, setMessage] = useState('Hello world');
 
  const onClick = () => {
    setMessage(() => {
      // => this error will be caught by the error boundary
      message.xyz();
      return message;
    });
  };
 
  return (
    <ErrorBoundary>
      <h1>{message.xyz()}</h1>
      <button onClick={onClick}>Click me</button>
    </ErrorBoundary>
  );
};

Solution

If you still want to show the fallback UI when an error occurs in an event handler, a great solution is to use the react-error-boundary library.

I've been using this library in every React app I've worked on. It's a great library—and its author is a former React core team member.

The react-error-boundary library provides many useful features for error handling in React apps, and it's very easy to use.

For example, you can use the ErrorBoundary and the useErrorHandler hook to handle errors in the event handler:

import { ErrorBoundary, useErrorHandler } from 'react-error-boundary';
 
const ErrorComponent = () => {
  const [message, setMessage] = useState('Hello world');
  const { showBoundary } = useErrorHandler();
 
  const onClick = () => {
    try {
      message.xyz();
    } catch (error) {
      // => this will show the fallback UI of the nearest ErrorBoundary
      showBoundary(error);
    }
  };
 
  return (
    <ErrorBoundary FallbackComponent={SomeErrorFallbackUI}>
      <h1>{message}</h1>
      <button onClick={onClick}>Click me</button>
    </ErrorBoundary>
  );
};

That's it—and now you have robust error handling in your React app.

Everything you need for handling errors in a React app is provided by react-error-boundary; just be sure to read its documentation and use it properly.

Happy coding! 🚀