66072aa532802b489d5c
·5 min read

Why I Love SWR Most: A Developer's Perspective

react
swr
react-query
4d5fd787ac74e0caa4f7

Sohan R. Emon

Developer, Learner, Tech Enthusiast

In this guide, we'll dive into using SWR (Stale-While-Revalidate) in React, a tool that streamlines data fetching, caching, and updating. SWR simplifies the process, allowing you to focus more on your app's functionality.

Installation

To kick things off, you need to install SWR using npm or yarn.

bash
npm i swr

Basic Usage

Once SWR is in place, you can employ it to fetch data from an API. Here's how you can do it in a React component:

jsx
// Syntax
  const { data, error } = useSWR(key, fetcher);

If key is null then swr won't run the fetcher

Here the key can be any function or property. And the fetcher function will receive the key as it's arguement.

jsx
import useSWR from 'swr';

function MyComponent() {
  const { data, error } = useSWR('/api/data', fetcher);

  if (error) return <div>Error loading data</div>;
  if (!data) return <div>Loading...</div>;

  return <div>Data: {data}</div>;
}

export default MyComponent;

We import useSWR from SWR and use it to retrieve data from the '/api/data' endpoint. Also, notice that we've defined a fetcher function that handles the actual HTTP request.

The fetcher Function

The fetcher function is responsible for making the API request and returning the data. Here's what a typical fetcher function might look like:

jsx
async function fetcher(url) {
  const response = await fetch(url);

  if (!response.ok) {
    throw new Error('Network response was not ok');
  }

  return response.json();
}

The fetcher function takes a URL, performs the request, and returns the data fetched from that URL. It also handles errors, throwing an error if the response is unsuccessful.

Arguments

key will be passed to fetcher as the argument. So the following 3 expressions are equivalent:

jsx
useSWR('/api/user', () => fetcher('/api/user'))
useSWR('/api/user', url => fetcher(url))
useSWR('/api/user', fetcher)

Multiple Arguments

jsx
const { data: user } = useSWR(['/api/user', token], ([url, token]) => fetchWithToken(url, token))

Mutate

mutate(key) or just mutate() will trigger a revalidation same as refetch from react-query.

Mutate Params

  • key: same as useSWR's key
  • data: data to update the client cache
  • options:
jsx
{
	optimisticData: {}, // data to immediately update the client cache
	revalidate: true, // revalidate once the mutation request resolves
	rollbackOnError: true, // rollback to previous cache if request rejected
}

Data Validation and Revalidation

SWR offers options for data validation and revalidation, allowing you to control when data is refreshed. For instance:

jsx
const { data, error } = useSWR('/api/data', fetcher, {
  refreshInterval: 60000, // Refresh every 60 seconds
  revalidateOnMount: true, // Revalidate when the component mounts
});

Error Handling

SWR makes error handling straightforward. It provides an error property in the return object, which allows you to gracefully handle errors. Here's an example:

jsx
const { data, error } = useSWR('/api/data', fetcher);

if (error) {
  return <div>Error: {error.message}</div>;
}

Automatic Revalidation

SWR excels at automatically revalidating data based on a set interval or conditions. You can specify a revalidateOnFocus option to automatically revalidate data when the application regains focus:

jsx
const { data } = useSWR('/api/data', fetcher, {
  revalidateOnFocus: true, // Revalidate data when the tab regains focus
});

This can be particularly useful for real-time applications that rely on up-to-date data.

Prefetch

preload is used to prefetch the resources programmatically and store the results in the cache. preload accepts key and fetcher as the arguments.

It can be called outside of React, inside hooks or in event handlers.

jsx
// Preload the resource before rendering the your component,
preload('/api/user', fetcher)
// Preload in effects
useEffect(() => {
  preload('/api/user?id=' + userId, fetcher)
}, [userId])
// Preload in event callbacks
<button  onClick={() => preload('/api/user?id=' + userId, fetcher)}>Load</button>

Dependency Tracking

This is another feature I love. SWR offers the ability to track dependencies. This can help keep your application state consistent. Here's a simplified example:

jsx
const { data: userData } = useSWR('/api/user', fetchUser);
const { data: ordersData } = useSWR(() => '/api/orders/' + userData.id, fetchOrders);

In this code, when the user data changes, SWR will automatically revalidate the orders data because of the dependency relationship established by the second useSWR call.

Infinite Scrolling

SWR can be used to implement infinite scrolling. It can load more data as the user scrolls down a page, providing a smooth user experience. Here's a simplified example:

jsx
const { data, size, setSize } = useSWRInfinite(
  (pageIndex, previousPageData) => {
    if (previousPageData && !previousPageData.hasMore) return null; // No more data
    return `/api/data?page=${pageIndex}`;
  },
  fetcher
);

const loadMore = () => {
  setSize(size + 1);
};

// Render the data and a button to load more

Offline Support

SWR has built-in support for offline data access. If the user loses internet connectivity, SWR will attempt to serve data from the local cache, providing a seamless offline experience.

Why I Prefer SWR Over React Query

Simplicity and Lightweight Nature

SWR is remarkably simple and lightweight. It doesn't introduce much overhead, making it easy to get started quickly. The learning curve is minimal, and you can start fetching data with just a few lines of code. It feels more like a natural extension of React itself.

In contrast, React Query, while feature-rich, may be seen as a bit more complex, with additional concepts to grasp, like query keys and mutations.

Data Fetching with a Hook

SWR's approach to data fetching via a hook, useSWR, fits seamlessly into the React component model. It feels intuitive and aligns with React's principles. With React Query, data fetching is based on hooks as well, but it may require more boilerplate, especially for complex use cases.

Play with Examples here

Found this useful?