at logo

Error handling in react

How do you hand errors consistently and in a nice and clean manner?

Where you want to handle specific errors you can use a try catch block

try {
  // some code that might throw an error
} catch (error) {
  // handle error
}

This is great except it won't catch errors from async code, unless your using async and await. Using regular promises you will still have to use .catch() to handle any errors thrown from the promise.

With the introduction of React 16 along came error boundaries which allow you to catch any errors from within your react child component tree, handle them however you like and display an alternative UI.

An error boundary is simply a class component that sits in your component tree and catches any errors from components below it in the tree, with some caveats, it cannot catch errors from async code or error handlers, server side rendering or errors from within the error boundary component itself.

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

  componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

An error boundary must be a class component and contain one of these methods componentDidCatch or getDerivedStateFromError.

Now we have single place to catch and handle our applications errors.

We can tidy this up with the use of the simple react-error-boundary package that simplifies the creation of an error boundary to a function component

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

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

const ui = (
  <ErrorBoundary
    FallbackComponent={ErrorFallback}
    onReset={() => {
      // reset the state of your app so the error doesn't happen again
    }}
  >
    <ComponentThatMayError />
  </ErrorBoundary>
);

It also provides some additional helpful features like the ability to capture and log any errors and also a simple way to reset the application state allowing us to recover nicely from the error.

Theres also a useErrorHandler hook which returns a function we can use to handle any errors from within our async code

const handleError = useErrorHandler();

function handleSubmit(event) {
  event.preventDefault();
  const name = event.target.elements.name.value;
  fetchGreeting(name).then(
    (newGreeting) => setGreeting(newGreeting),
    handleError
  );
}

A tidy and consistent way to handle any application errors from within your react application.

timney.net
Site published