In React, external data is very important to make your project more dynamic. However, when you use useEffect
for data fetching, it is generally hard to manage the state, fortunately SWR simplifies this process.
Introduction to SWR
The first thing I want to say is what SWR means.
According to its documentation:
The name “SWR” is derived from
stale-while-revalidate
, a HTTP cache invalidation strategy popularized by HTTP RFC 5861. SWR is a strategy to first return the data from cache (stale), then send the fetch request (revalidate), and finally come with the up-to-date data.
So, SWR is a library that provides React hooks for data fetching.
Implementing SWR in Your Project: My Experience
I recently completed the first project for the Front End Libraries Certification of FreeCodeCamp. I tried SWR in this project and will share my experience.
Note: For more information, you can check the project's GitHub repository and CodeSandbox.
I used TypeScript for this project, but I will give you examples with JavaScript. So, don't worry if you don't know TypeScript. (I highly recommend it if you don't know.)
1. Creating fetcher
function
I was planning to use Axios for the requests, so I created the async function below.
const fetcher = async (url) => {
const { data } = await axios.get(url);
return data;
};
This function is just a wrapper for the get
method of Axios. It accepts a url
parameter and returns data
.
2. Creating a hook that fetches a random quote
Based on the section Make It Reusable in SWR documentation, I created a hook called useRandomQuote
.
Note: In this project, I used Quotable API for random quotes.
This hook is a wrapper for useSWR
hook.
In my case, the API returns an array with one element which is the random quote. Since data
is undefined
during the request, I used optional chaining.
And, the hook returns an object that contains the quote
variable with the variables destructured from useSWR
hook.
Let's check the code and inspect it:
import useSWR from 'swr/immutable';
const useRandomQuote = () => {
const { data, ...restSWR } = useSWR(
'https://api.quotable.io/quotes/random',
fetcher
);
return {
...restSWR,
quote: data?.[0],
};
};
export { useRandomQuote };
In your projects, you almost always use useSWR
hook.
Take a closer look at the useSWR hook:
const { data, error, isValidating, isLoading, mutate} = useSWR(
key,
fetcher
);
Firstly we imported
useSWR
hook.-
Then we used it in our custom hook with two parameters:
key
andfetcher
.key
is a unique string for the request which is like an id.fetcher
is an async function that acceptskey
and returnsdata
. -
Finally, it returns
data
,error
,isValidating
,isLoading
, andmutate
variables.data
is the variable returned fromfetcher
. During the first request, it will beundefined
.error
is the error thrown byfetcher
. If there is no error, it will beundefined
.isValidating
is a boolean that indicates the request's status. It istrue
during each request including the first request.isLoading
also is a boolean that indicates the first request's status. It istrue
during the first request, then it will always befalse
.mutate
is a function that is used to modify the data manually.
If you noticed, I imported useSWR
hook from swr/immutable
in the first code snippet. It simply disables SWR's auto-revalidation. I prefer to use it because I want to revalidate the data manually with mutate
function.
Understanding Auto-Revalidation
If the data you used is stale, SWR revalidates the data (re-fetches from the server) to keep freshness.
By default, SWR automatically revalidates the data (It assumes that the data is stale) in three cases:
Every time the component is mounted, even if there is data in the cache, it revalidates.
It revalidates when the window gets focused.
It revalidates when the browser regains its network connection.
3. Mutating the Data
If you want manually revalidate the data, you can use mutate
function.
I have QuoteCard
component that displays the current quote. This component has a New Quote
button that handles the mutation of the quote.
const QuoteCard = () => {
const { quote, isValidating, mutate } = useRandomQuote();
return (
{/* ... */}
<button
// ...
onClick={() => mutate()}
>
New Quote
</button>
{/* ... */}
)
}
When the mutate function is called, SWR revalidates the data. During this process, the quote
variable remains the same (will not be undefined), and the isValidating
variable becomes true
.
Here is a diagram of this process:
4. Handling Concurrent Requests
If you don't handle request cancellation manually, the ongoing request continues even if the user initiates a new request alongside it, resulting in multiple concurrent requests.
SWR only updates the data after the last request is completed. If there are concurrent requests, their responses aren't important except for the last one. So if you don't cancel these requests, it results in unnecessary network usage.
I faced the same issue in my project. When the user clicks the New Quote
button while a request is still loading, the application triggers a new request alongside the existing one.
Although I checked SWR's documentation, I couldn't find a built-in solution for this issue. (I also checked React Query, and it has a built-in solution for that. I am planning to experiment with it in my next project involving API usage.)
In this scenario, I could use AbortController
to handle request cancellation, but I didn't use it to keep things simple. I just disabled the New Quote
button during validation.
Conclusion
SWR helped me a lot in this project. If you have not used it previously, I highly recommend trying it in your future projects.
This is my first blog post, and I will be waiting for your feedback and thoughts about the library in the comments.
Stay in Touch
Resources
Revalidating options of SWR by Toru Kobayashi
My inspiration to use this library comes from this video by CoderOne. (I know this video is about React Query, but I wanted to try SWR first.)
Thanks, I did not know about
swr/immutable
, good to know that this exists.Regarding the cancellation, did you test what SWR does when you call mutate while the request is still loading? I think it updates with the data from the first call when it is returned first and then gets overridden by the second response of the secong mutate/request right?
Cancelling would also come with issues, if the API response is slower then the frequency of calling mutate it would never show a result.
The only real issue would be if the first mutate gets a response later (after the response originating from the second mutate call) and overrides the data of the second response which was returned faster. But I am quite confident this is handle correctly by SWR. Didn’t find information about this behavior in the docs though.
I did recently use SWR in a Chrome Extension for accessing local storage, also very handy.
You can spread the remaining properties of useSWR in your hook you might be more future proof and it is a little less code, like so: