Supercharge Your Data Fetching with Optimistic UI, Preload, and Error Handling Using SWR
Tags: react, swr
Published: 2023-06-02 00:08:57

**SWR (Stale-While-Revalidate)** is a powerful library that provides a robust solution for data fetching, caching, and error handling. In this blog, we will dive into the core concepts of SWR, including optimistic UI, preloading data, and handling errors with rollbackOnError. We'll explore each concept in detail and provide code examples to demonstrate their usage and benefits.

**SWR and Data Fetching:**
SWR is a JavaScript library that leverages the concept of _"stale-while-revalidate"_ to fetch and cache data. It aims to provide a seamless user experience by displaying cached data while simultaneously revalidating it in the background. This approach improves performance and responsiveness by minimizing unnecessary requests.

Let's consider an example of fetching user data using SWR:

```javascript
import useSWR from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

function UserProfile({ userId }) {
  const { data, error } = useSWR(`/api/users/${userId}`, fetcher);

  if (error) {
    return <div>Error fetching user data.</div>;
  }

  if (!data) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.email}</p>
      {/* Render other user details */}
    </div>
  );
}
```

In this example, `useSWR` is used to fetch user data from the specified API endpoint (`/api/users/${userId}`). The `fetcher` function is responsible for making the HTTP request and returning the JSON response. The `data` and `error` returned by `useSWR` are used to conditionally render the UI based on the data fetching status.

**Optimistic UI:**
Optimistic UI is a powerful technique to provide instant feedback to users by optimistically updating the UI before the server responds. SWR makes it easy to implement optimistic UI by allowing you to mutate the data cache directly.

Let's extend the previous example to demonstrate optimistic UI:

```javascript
import useSWR, { mutate } from 'swr';

// ...

function UserProfile({ userId }) {
  // ...

  const handleFollow = async () => {
    // Optimistic update
    mutate(`/api/users/${userId}`, { ...data, isFollowed: true }, false);

    // Make the actual API request
    try {
      await fetch(`/api/users/${userId}/follow`, { method: 'POST' });
      // Revalidate the data to fetch the latest changes
      mutate(`/api/users/${userId}`);
    } catch (error) {
      // Rollback on error
      mutate(`/api/users/${userId}`);
    }
  };

  return (
    <div>
      {/* Render user details */}
      {data.isFollowed ? (
        <button disabled>Following</button>
      ) : (
        <button onClick={handleFollow}>Follow</button>
      )}
    </div>
  );
}
```

In this example, the `handleFollow` function is triggered when the user clicks the "Follow" button. It first optimistically updates the UI by modifying the local `data` cache using `mutate`. Then, it makes the actual API request to follow the user. If the request is successful, the data is revalidated to fetch the latest changes. In case of an error, the cache is rolled back to maintain consistency.

**Preload:**
Preloading data is a crucial technique to improve perceived performance by fetching essential data in advance. SWR provides the `mutate` function with a `data` argument, allowing us to preload data into the cache.

Consider an example where we preload user data before rendering the `UserProfile` component:

```javascript
import { SWRConfig, mutate } from 'swr';

// ...

function App() {
  useEffect(() => {
    const userId = 123;
    fetch(`/api/users/${userId}`).then((res) => {
      if (res.ok) {
        return res.json().then((data) => {
          // Preload the data into the cache
          mutate(`/api/users/${userId}`, data);
        });
      }
    });
  }, []);

  return (
    <SWRConfig value={{}}>
      <UserProfile userId={123} />
    </SWRConfig>
  );
}
```

In this example, the `App` component preloads user data by making an initial request to `/api/users/${userId}` and storing the response in the cache using `mutate`. The `UserProfile` component is then rendered inside the `SWRConfig` provider, which enables SWR and allows the component to utilize the preloaded data.

**RollbackOnError:**
Handling errors gracefully is vital for a seamless user experience. SWR provides the `rollbackOnError` option to automatically revert the cache to its previous state in case of an error.

Let's modify our previous example to rollback the cache on error:

```javascript
import useSWR from 'swr';

// ...

function UserProfile({ userId }) {
  const { data, error } = useSWR(`/api/users/${userId}`, fetcher, {
    rollbackOnError: true,
  });

  // ...
}
```

By passing the `rollbackOnError` option as `true`, SWR will automatically rollback the cache to its previous state if an error occurs during data fetching. This ensures that the UI remains consistent and avoids displaying incorrect or outdated data.

SWR simplifies data fetching, caching, and error handling by leveraging the "stale-while-revalidate" concept. In this blog, we explored the core concepts of SWR, including data fetching, optimistic UI, preloading data, and rollbackOnError. By implementing these techniques, you can create high-performance web applications with seamless user experiences. SWR empowers developers to build efficient and responsive interfaces while keeping the codebase clean and maintainable.

Remember to refer to the official SWR documentation for further details and explore additional features offered by the library. Happy coding!