Boost Your React App's UX: Comprehensive Error Handling with Error Boundaries

rouretl

Lucas ROURET

Posted on August 7, 2024

Boost Your React App's UX: Comprehensive Error Handling with Error Boundaries

In this week's article, we will explore solutions for handling errors in React applications. Managing errors is crucial for delivering a quality user experience. Unhandled errors, like the absence of feedback when a button is pressed, can negatively impact user satisfaction. To enhance the user experience (UX), it is essential to avoid a lack of response or information when clicking a button or displaying a default 500 error. Today, we have several options for handling errors:

  • Using .catch on a promise for API calls.

  • Using the error option of React Query.

  • Using "Error Boundaries," which is what I will discuss this week.

To start, we will use a mock website and improve the code to achieve the best possible error management.


✏️ Creating a Mock Application: Setting the Stage for Error Handling

The application will fetch a list of users to display later.

Creating a User's hook

For the connector, we will use a non-existent domain name such as "fakeapiontheinternet.com" which will throw an error (we could have used a promise with a throw, but here I want to take a concrete example).

export const useUsers = () => {
  const { data } = useSuspenseQuery<Array<User>>({
    queryKey: ["users"],
    queryFn: () =>
      fetch("https://fakeapiontheinternet.com/users").then(
        (res) => res.json() as Promise<Array<User>>
      ),
  });

  return {
    users: data,
  };
};
Enter fullscreen mode Exit fullscreen mode

We use React Query for the article. If you want to know more about useSuspenseQuery, read my article about Stop using useQuery from React-Query !

Structuring the Page: Building the Foundation

function App() {
  const { users } = useUsers();

  return (
    <div className="container">
      <div className="content">
        {users.length === 0 && <p>Empty ....</p>}
        {users.length !== 0 && users.map((u) => <p>{u.name}</p>)}
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Explanation of the Code Above

  • Using the previously defined hook:
const { users } = useUsers();
Enter fullscreen mode Exit fullscreen mode
  • Displaying users based on the state value: "Empty ..." if there are no users, or the usernames of the users.
 {users.length === 0 && <p>Empty ....</p>}
 {users.length !== 0 && users.map((u) => <p>{u.name}</p>)}
Enter fullscreen mode Exit fullscreen mode

👀 Code Decryption: understanding and anticipating Errors

There is a concept I really like in development: "First Run First Win." The principle is very simple. It's like a little game against yourself that leads to significant reflection and learning. For example, in our case, you coded with me, but you don't know the result. Try to play the game and guess the result of this:

What happens when I load the page?

Have you played the game? 🎰


The answer: Nothing happens!

Screen with empty content but error in JS console

Not exactly nothing!

Schema explain that useUsers throw an error

If we analyze our code, our 'useUsers' hook has generated an error, which has halted the execution of the rest of the code. That's why we don't even see the "Empty ...".

Put yourself in the user's shoes; this situation is so frustrating!

🛡️ Error Boundaries: React's Built-in Solution for Error Handling

Error boundary is a feature in React. To quote:

By default, if your application throws an error during rendering, React will remove its UI from the screen. To prevent this, you can wrap a part of your UI into an error boundary. An error boundary is a special component that lets you display some fallback UI instead of the part that crashed—for example, an error message.

This exactly addresses our problem!

Schema explain how to use Error Boundary in our example

To handle our error, we will:

  • Create the "UserList" component which will call our hook.
  • In <App />, we will wrap <UserList /> with <ErrorBoundary />.

Personally, I work with React and React Native. The 'react-error-boundary' library is perfect because it ensures compatibility with both React and React Native.

The ErrorBoundary component has several properties:

  • fallback: The simplest way to render a default "something went wrong" type of error message.
  • fallbackRender: A function responsible for returning a fallback UI based on the thrown error.
  • FallbackComponent: A React component responsible for returning a fallback UI based on the thrown error.
  • onError: For logging.

Implementing Error Boundaries: Step-by-Step Guide

Let's create the UserList component, containing our previous code for displaying users.

const UserList = (): JSX.Element => {
  const { users } = useUsers();

  return (
    <div className="content">
      {users.length === 0 && <p>Empty ....</p>}
      {users.length !== 0 && users.map((u) => <p>{u.name}</p>)}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Let's update the content of our App to call the UserList component.

function App() {
  return (
    <div className="container">
      <UserList />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Integrating Error Boundaries: Enhancing User Experience

Integrating Error Boundaries: Enhancing User Experience

<ErrorBoundary fallback={<div>Something went wrong</div>}>
     <UserList />
  </ErrorBoundary>
Enter fullscreen mode Exit fullscreen mode

We use ErrorBoundary from the "react-error-boundary" library to catch and handle errors in our UserListcomponent. The fallback prop is used to render a simple error message ("Something went wrong") when an error occurs. This ensures that the entire UI does not crash and provides a better user experience by displaying a meaningful message instead of a blank screen or a default error page.


Screen with Something went wrong at the center

Now, we have an error message ✨

We can now define our ErrorView and change the fallback property to FallbackComponent.

const ErrorView = ({ error, resetErrorBoundary }: FallbackProps) => {
  return (
    <div>
      <h1>Something went wrong</h1>
      <p style={{ color: "red" }}>{error.message}</p>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
};

function App() {
  return (
    ...
      <ErrorBoundary FallbackComponent={ErrorView}> <---
        <UserList />
      </ErrorBoundary>
    ...
  );
}
Enter fullscreen mode Exit fullscreen mode

Error view

💡 The code for my articles is available on this REPO

🔥 Conclusion: Mastering Error Handling for a Seamless User Experience

Mastering error handling in React is crucial for providing a seamless user experience. Techniques like using .catch on promises, leveraging the error option in React Query, and utilizing Error Boundaries ensure that your application remains robust and user-friendly even when unexpected errors occur. Error Boundaries, in particular, provide a powerful way to catch and handle errors gracefully, preventing your entire UI from crashing and allowing you to display meaningful fallback content. This not only improves the user experience but also makes your code more maintainable and elegant. By adopting these practices, you can create more resilient React applications that keep users engaged and satisfied.

In conclusion, transitioning to useSuspenseQuery and ErrorBoundary from traditional isLoading and error handling methods offers a more elegant and maintainable approach. It enhances both the developer experience and the end-user experience by ensuring that errors are managed gracefully and the application remains robust. By mastering these error handling techniques, you can build React applications that are not only resilient but also provide a seamless and satisfying user experience.

Hi, I'm Lucas Rouret, a Web and Mobile Software Engineer passionate about technology since I was 15. I mainly work with JavaScript and TypeScript. Currently, I'm developing a browser-based hero management game similar to "Raid Shadow Legends."

Interested in my content? Follow me on X (Twitter) and subscribe to my newsletter for weekly posts and detailed development logs. Join a community of tech enthusiasts now!

💖 💪 🙅 🚩
rouretl
Lucas ROURET

Posted on August 7, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related