React Error Boundary mistake

3 months ago
|
0 views

TL;DR: React ErrorBoundary only catch the error that happens during the rendering phase, not the error that happens in the event handler. Use react-error-boundary library to handle the error in the event handler.

When I read about the react's error boundary, I thought that I just need to wrap my component with ErrorBoundary and every single Javascript error will be caught and handled by the it. Because it's make sense, right ?

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

React's ErrorBoundary

Let's say you already have a stand 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 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 expect that when you click the button, the error will be caught by the ErrorBoundary and fallback will be rendered, right ?

But it's not how it works.

The ErrorBoundary only catch the error that happens during the rendering phase, not the error that happens in the event handler, like the onClick event in the example above.

Legay React Error Boundaries docs clearly states 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 above example, ErrorBoundary only catch if some error happen during the rendering phase, like this 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 the error happens in the event handler, a great solution for this is to use the react-error-boundary library.

I've been using this library in every single react app that I've been working on. Great library, as the author is a former react core team member.

react-error-boundary provides a lot of usefull features for error handling in react app, and it's very easy to use also.

As in example above, you can use the ErrorBoundary and useErrorBoundary hook to handle the error in the event handler:

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

Just that, and you will have a great error handling in your react app.

Everything you need for handling error in react app is provided by react-error-boundary, make sure you read the docs and use it properly.

Happy coding! 🚀