Understanding SWR in React: Basics to Advanced
In React app development, managing data is crucial. SWR (Stale-While-Revalidate) is a powerful data fetching and caching library that simplifies data retrieval and state updates in applications. This article will start with basic concepts of SWR and then delve into how to use it in React for handling data fetching and state management.
SWR is a React Hooks library that offers a simple yet powerful way to handle data fetching and caching. Its core idea is to provide stale data while fetching, and update data in the background to keep it fresh. This pattern not only improves application performance but also enhances user experience.
The simplest way to use SWR is through the useSWR
hook function. In TypeScript, defining data types and implementing generics is easy.
import useSWR from 'swr';
interface UserData {
id: number;
name: string;
}
const fetcher = (url: string) => fetch(url).then(res => res.json());
export function Nav() {
const { data, error, isLoading } = useSWR<UserData>('/api/user', fetcher);
if (error) return <div>Failed to load</div>;
if (isLoading) return <div>Loading...</div>;
return (
<div>
<h1>Hello, {data.name}!</h1>
</div>
);
};
Also, you can use fallback data to return something earlier than the real query.
const { data, error, isLoading } = useSWR<Post>(`/api/post/${postId}`, fetcher, {
fallbackData: {
id: postId,
title: 'Loading...',
content: 'Loading...',
},
});
SWR also provides global configuration options to customize its behavior. For instance, you can configure global cache time (default is 0ms, but if set, all SWRs under this configuration will automatically request at intervals, essentially polling), error retry strategies, etc.
import { SWRConfig } from 'swr';
export function App({ children }) {
return (
<SWRConfig
value={{
refreshInterval: 3000,
fetcher: (url: string) => fetch(url).then(res => res.json()),
}}
>
{children}
</SWRConfig>
);
};
With this configuration, you no longer need to write a separate fetcher function. However, if there's additional logic in the API, SWR itself supports overriding.
SWR also offers the mutate
method to manually update data in the cache, i.e., updating the data returned by useSWR. Paired with optimistic updates, this can provide a very smooth API experience.
import useSWR, { mutate } from 'swr';
const updateUser = async (newName: string) => {
await fetch('/api/user', { method: 'PUT', body: JSON.stringify({ name: newName }) });
mutate('/api/user'); // Update data in cache
};
However, here you're actually using the global mutate function. You only need to write a corresponding key in the mutate function, which allows you to update data from other APIs. This is because SWR itself uses a hash table to store all request keys and puts the results together.
So, if you just want to call the current API again, it's more appropriate to use the mutate function exported by useSWR itself, like this:
const { data, mutate } = useSWR('/api/user', fetcher)
But if you want to call the global mutate, there's a better way:
import useSWR, { useSWRConfig } from "swr";
function Profile() {
const { mutate } = useSWRConfig();
const { data } = useSWR("/api/user", fetcher);
const handleClick = async () => {
const newName = data.name.toUpperCase();
const user = { ...data, name: newName };
const options = {
optimisticData: user,
rollbackOnError(error) {
// Don't roll back if the error is due to timeout
return error.name !== "AbortError";
},
};
// Update local data immediately
// Send a request to update data
// Trigger revalidation to ensure local data correctness
mutate("/api/user", updateFn(user), options);
};
return (
<div>
<h1>My name is {data.name}.</h1>
<button onClick={handleClick}>Uppercase my name!</button>
</div>
);
}
The code above is already paired with optimistic updates, allowing us to make rapid modifications using predicted results, providing users with a silky smooth experience.
SWR boasts additional features such as conditional fetching and support for multiple arguments.
If you're interested in exploring these functionalities further, feel free to give them a try yourself!