One of the reasons why I write these articles is to fortify the knowledge of new concept I recently learned, while trying to apply that knowledge to everyday tasks I have to do as a developer.
And one of the most common things you do as a developer is fetching some data from an API and present it on a client.
And I already wrote about that in the past, with "Fetching data with React hooks and Axios" and just the other day I published "A practical example of Suspense in React 18" on the same topic.
But the latter article was using a very manual approach, where you write your wrapper around the fetching library to use the new Suspense
component in React 18, but it's not the only way, and there's a new tool in the block that can make that same job way more simple and easy to use: SWR.
But what is SWR?
In the project own words:
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.
It's not a data fetching library for sure, it does the same job as the wrappers I talked this previous article, and it let you use a simple hook to simplify the fetching process and how to handled it in a react functional component.
But on top of that it also cache it, so if you are requesting the same endpoint multiple times, it checks if the internal cache already the data you need (and if it's still valid), improving the overall performances of your application.
Let's refactor our code
Install the package
As usual, first thing to do is to install it, so:
npm install swr --save
The starting point
So previously we had the following components and libraries:
- A wrapper for our fetching library that was throwing exception when the fetching promise was not resolved, so it was in a
pending
state, or it was rejected. Example here. - A wrapped fetching logic, where we used
axios
to call an API to get our data, and it was wrapped by the function above. Example here - A child component that is calling the function to fetch the data and it renders the code with it.
- A parent component that uses Suspense with a fallback component, which it will be shown until the fetching promise is resolved, once that done, the child component will be rendered instead. Example here.
What we need to change?
So, the wrapper and wrapped function can go, we don't need that anymore.
The parent component will be unchanged, as everything will happen in the child component.
Our actual job will be to just refactor the child component, and the current code will look like this:
import React from 'react';
import fetchData from '../../api/fetchData.js';
const resource = fetchData('/sample.json');
const Names = () => {
const namesList = resource.read();
return (
<div>
<h2>List of names</h2>
<p>This component will use a custom handler for fetching data.</p>
<ul>
{namesList.map(item => (
<li key={item.id}>
{item.name}
</li>))}
</ul>
</div>
);
};
export default Names;
As we said, we can get rid of the old fetching logic, so this import line can be replaced with swr
and axios
import:
// from this
import fetchData from '../../api/fetchData.js';
// to this
import useSWR from 'swr';
import axios from 'axios';
Now we still need to use axios
to fetch our data, and this will replace our resource
object we had before:
// from this
const resource = fetchData('/sample.json');
// to this
const fetcher = url => axios.get(url).then(({data}) => data);
Here I'm using axios
, but what library to use is up to you, you can use any fetching library you want as long it does return a promise with the data we want to read.
So far all of this is happening outside the component code, and while with the old logic we used to call a read()
method from our resource
object, like this:
const Names = () => {
const namesList = resource.read();
Now we need to use the useSWR
hook instead, but there's a catch: in order to use the suspense
component, you need to pass a parameter to tell SWR to support that:
const Names = () => {
const { data: namesList } = useSWR(
'/sample.json',
fetcher,
{ suspense: true}
);
Some of you might wonder "Why I can just pass the url directly to the fetcher callback function?". The reason is because SWR will use that key as a cache key, so next time you will call that same endpoint, it will return the cached value.
And that's it! The rest of the code will be identical!
So the final children component code will look like this:
import React from 'react';
import useSWR from 'swr';
import axios from 'axios';
const fetcher = url => axios.get(url).then(({data}) => data);
const Names = () => {
const { data: namesList } = useSWR('/sample.json', fetcher, { suspense: true});
return (
<div>
<h2>List of names with SWR</h2>
<p>This component will use the SWR hook for fetching data.</p>
<ul>
{namesList.map(item => (
<li key={item.id}>
{item.name}
</li>))}
</ul>
</div>
);
};
export default Names;
The parent component will still be unchanged, as everything is happening in the children component, but in case you want to see how everything is wrapped with Suspense
, here the code:
import React, { Suspense } from 'react';
import Names from './Names';
import Loading from '../Loading';
const Home = () => (
<div>
<h1>Best devs:</h1>
<Suspense fallback={<Loading />}>
<Names />
</Suspense>
</div>
);
export default Home;
I hope this article helped you to understand how to use SWR with the new Suspense
component in React 18 ;-)
Top comments (1)
Hey alessio, the article is amazing, just a small doubt, on re-render, will the hook be called again, and is there any lazy fetcher function that calls the api.