Frontend Development

Data Fetching In React With React SWR

Data Fetching In React With React SWR

Fetching data is one of the important needs in web applications. The same applies in react as a javacript framework, data fetching can be done with many techniques. In this article we will learn about react swr library for data fetching.

 

 

 

Before digging into swr, let’s check a basic example where we fetch some dummy data into a react app.

PostList.jsx

import React, { useEffect, useState } from "react";

const base_url = 'https://dummyjson.com';

function PostList()
{
    const [posts, setPosts] = useState([]);
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState(null);

    useEffect(() => {
        const fetchData = async (url) => {
            setIsLoading(true);

            try {
                const data = await fetch(base_url + url)
                .then(res => res.json());

                setPosts(data ? data.posts : []);
            } catch(error) {
                setError('Failed to load');
            }
            

            setIsLoading(false);
        }

        fetchData('/posts');
    }, []);

    return (
        <>
            {
                error ? <div>{error}</div> : null
            }

            {
                isLoading ? <div>loading...</div> : null
            }

            {
                posts ? posts.map(post => {
                    return (
                        <div key={post.id}>
                            <h3><a>{post.title}</a></h3>
                            <p>{post.body}</p>
                            <span>Views: {post.views}</span>
                            <div>Tags: {post.tags.map(tag => <span key={tag}>{tag}</span>)}</div>
                        </div>
                    )
                }) : null
            }
        </>
    )
}

export default PostList;

In this example component we are fetching some dummy data from http://dummyjson.com. As a react developer you already familiar with this approach for fetching data as we declared some state variables to hold the loading, error, and posts states. 

I used React useEffect() hook to fetch the data on page load and defined a function fetchData() which make use of the Javascript fetch Api to fetch data from remote source. I wrapped the code with try..catch block so that we can handle fetching errors. Then i populated the state variables like posts, loading, and error. Finally i displayed the data.

 

Note how many steps we followed to make data fetching for this simple example. Now let’s accomplish this but using React SWR.

Install React SWR:

npm i swr

Or with yarn:

yarn add swr

Or with pnpm:

pnpm add swr

Let’s refactor our component to use SWR:

PostList.jsx

import React from "react";
import useSWR from 'swr';

function PostList()
{
    const base_url = 'https://dummyjson.com';

    const fetcher = (url) => {
        return fetch(base_url + url).then(res => res.json());
    };

    const { data, error, isLoading } = useSWR('/posts', url => fetcher(url));

    return (
        <>
            {
                error ? <div>failed to load</div> : null
            }

            {
                isLoading ? <div>loading...</div> : null
            }

            {
                data ? data.posts.map(post => {
                    return (
                        <div key={post.id}>
                            <h3><a>{post.title}</a></h3>
                            <p>{post.body}</p>
                            <span>Views: {post.views}</span>
                            <div>Tags: {post.tags.map(tag => <span key={tag}>{tag}</span>)}</div>
                        </div>
                    )
                }) : null
            }
        </>
    )
}

export default PostList

I imported the useSWR hook. This hook does all the job for us and return the response which is data, the error if found and loading status. As you see how we simplify the above code and remove all the state variables by using this hook. 

Let’s take a look at the useSWR(key, fetcher, options) parameters:

  • key: a unique key to identify the request, can be string, function, array, object or null. In the previous example i passed “/posts” as the key but it can be any unique string not used anywhere else in the application, for example it can be “all_posts”.
  • fetcher: an optional fetcher callback function, which return a promise.
  • options: an optional options object to customize the SWR behavior. 

Most of the time you will be using the key and the fetcher function. The key is very important identifying the request, so make it unique.

The key also is important because it SWR uses the key to cache the request response, so every time you make subsequent requests you will get the cached data for some time thereby we take the benefit of not making many requests.

The options parameter is where you customize the SWR behavior, you can learn more about the available options in the SWR docs.   

 

SWR Fetcher Function

The second parameter in SWR is the fetcher function which contains the actual logic for making http requests and return the data. In the previous example we invoked useSWR() like so:

useSWR('/posts', url => fetcher(url))

The key here will be passed to the fetcher function as an argument, so the url argument will contain “/posts” string. For me i prefer this way as you identify the request using the endpoint. However you can also invoke it this way:

useSWR('/posts', () => fetcher('/posts'))

Or

useSWR('/posts', fetcher)

What about if we want to use a dynamic key. An example for this to load the post details using post id or loading user profile by user id. As mentioned above that the key can also be an array or object.

Let’s demonstrate this by adding another component to load the posts details by id:

PostDetails.jsx

import React from "react";
import { useParams } from 'react-router-dom';
import useSWR from 'swr';

const base_url = 'https://dummyjson.com';

function PostDetails()
{
    let { postId } = useParams();

    const fetcher = async ([url, postId]) => {
        const res = await fetch(base_url + url + `/${postId}`);
        
        return res.json()
    }

    const { data: post, error,  isLoading } = useSWR(['/posts', postId], fetcher);

    return (
        <>
            {
                error ? <div>Failed to load</div> : null
            }

            {
                isLoading ? <div>loading...</div> : null
            }

            {
                post && (
                    <div>
                            <h3>{post.title}</h3>
                            <p>{post.body}</p>
                            <span>Views: {post.views}</span>
                            <div>Tags: {post.tags.map(tag => <span key={tag}>{tag}</span>)}</div>
                            <div>Likes: &#128077; {post.reactions.likes}</div>
                            <div>Deslikes: &#128078; {post.reactions.likes}</div>
                        </div>
                )
            }
        </>
    )
}

export default PostDetails;

For the purpose of the component i am using react router to pick the “postId” url segment from the url. Next update the PostList.jsx component to include a Link to navigate to PostDetails. 

PostList.jsx

import React from "react";
import { Link } from "react-router-dom";
import useSWR from 'swr';

PostList.jsx

....
....


function PostList()
{

   return (
      <>
      ...
      ...
        {
                data ? data.posts.map(post => {
                    return (
                        <div key={post.id}>
                            <h3><Link to={`/post-detail/${post.id}`}>{post.title}</Link></h3>
                            <p>{post.body}</p>
                            <span>Views: {post.views}</span>
                            <div>Tags: {post.tags.map(tag => <span key={tag} style={style}>{tag}</span>)}</div>
                        </div>
                    )
                }) : null
            }
      </>
   )
}

Next look at the useSWR() here we are passing an array of the endpoint url and the postId. This array in turn will be passed to the fetcher function like so:

const fetcher = async ([url, postId]) => {
}

This is how to use a dynamic parameters in the key.

 

SWR Error Handling

Any error triggered in the fetcher function will be returned as error from the useSWR() hook. And you can customize this error according to your fetching processing logic. In the previous component, let’s check for the response status:

const fetcher = async ([url, postId]) => {
        const res = await fetch(base_url + url + `/${postId}`);

        if (!res.ok) {
            const error = new Error('An error occurred while fetching the data.')

            error.info = await res.json()
            error.status = res.status
            throw error
          }
         
          return res.json()
    }

And update the jsx return of the error like so:

return (
    <>
      {
          error ? <div>{error.message}</div> : null
      }
      
      ....
      ....
    </>
)

To check this you can for example update the endpoint name from “/posts” to “/posts-not-found”, to trigger 404 not found error.

 

SWR Auto Revalidation

Revalidation is the process of marking the data as expired and trigger a re-fetch again. 

In auto revalidation, revalidation happens in these cases automatically:

  • Revalidate on Focus: Triggers on window focus or when switching between tabs.
  • Revalidate on Interval: Triggers according to some interval.
  • Revalidate on Reconnect: Triggers when the user is offline and back online. This is useful when the network connection and back again to fetch the latest data.

These cases can be controlled by configuration options in the useSWR() hook:

useSWR(key, fetcher, {
  revalidateIfStale: false,
  revalidateOnFocus: false,
  revalidateOnReconnect: false
})

Or you can use another hook SWR gives us which useSWRImmutable():

useSWRImmutable(key, fetcher)

There is also manual revalidation which you can check at the SWR docs.

 

Source Code

0 0 votes
Article Rating

What's your reaction?

Excited
0
Happy
0
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments