Data fetching in ReactJS/NextJS with useSWR

26 / Aug / 2024 by Mohd Ashraf 0 comments

“Steve Jobs once said, ‘Innovation distinguishes between a leader and a follower.’ The same applies to web development.”

So let’s go beyond useEffect and learn a new way of data fetching using useSWR. Before diving deep into the working of useSWR you first need to understand what useSWR is and why you need to use it over useEffect.

What is useSWR?

useSWR is a third-party React hook library (by Vercel) designed to simplify data fetching by providing built-in features like caching, revalidation, focus re-fetching, and error handling out of the box.

SWR stands for Stale-While-Revalidate. The Stale-While-Revalidate concept means that while SWR is revalidating the data, it serves the stale data from the cache, ensuring that your data is always fresh.

Why choose useSWR over useEffect?

Handling loading states, revalidating data, prefetching, error management, and managing multiple API calls with useEffect can be cumbersome. It often requires writing boilerplate code to handle loading states, caching, and error handling.

useSWR abstracts away much of this complexity, allowing you to focus on the logic specific to your application. Now you are going to understand how useSWR hook works and how you can implement it in your ReactJS/NextJS project.

Essentials of useSWR

useSWR accepts three parameters, the third parameter is optional which determines the way the data is handled and the first two parameters determine what is going to be fetched and how it will be fetched. Also, useSWR hook returns five values. We will discuss these params and values briefly in the upcoming section.

structure of useSWR

 

Parameters

  • key: key param is typically a string (often a URL) or an array or an object. The reason for the key name is because it actually is a unique identifier for the data being fetched. During global mutations, the key of a useSWR hook is used to make changes specific to that hook. So if you use the same key in different components, they will share the same cache.
  • fetcher: fetcher param is a function that is responsible for fetching the data and returns a promise.

fetcher function

You can also use ‘axios’ instead of fetch api. The argument url passed into the fetcher function is the key param of useSWR.

By default key is the only argument for the fetcher but what if you need more arguments for the fetcher function? In this case, you need an array as key which allows you to pass multiple arguments to the fetcher function. Let’s take an example to understand this better. Suppose you have an API endpoint that requires both a userId and a token to fetch user data.

fetcher function

This is not the correct way because url ‘/api/user‘ does not include userId or token, and if any of these changed useSWR would still use the same cached key and return the previous data as cached data is mapped with a unique key and must be unique.

To tackle this, you would have to pass these arguments either as an array or an object.

fetcher function

The url, userId, and token are now all included in the key parameter, so any changes to userId or token will cause useSWR to use a different cached key, ensuring that the correct data is provided.

  • options: The third optional parameter, options, is an object that allows you to customize how your fetched data is managed. This includes controlling when the data should be revalidated, when to retry fetching and specifying actions for both successful and failed requests. Below are some of the options properties:
    • refreshInterval: Takes a number and automatically re-fetch the data after the specified interval (in milliseconds). By default disabled (refreshInterval = 0).

refreshInterval

    • revalidateOnFocus: Takes boolean value and re-fetch the data when the window regains focus. By default true.

revalidateOnFocus

    • errorRetryInterval: Takes a number and waits till the specified interval (in milliseconds) before retrying a request after it fails. By default 5000 (5 seconds).

errorRetryInterval

    • errorRetryCount: Takes a number and controls the number of times useSWR will retry a request when an error occurs. By default 8.

errorRetryCount

    • onSuccess and onError: It’s a callback function that runs when the request succeeds or throws an error respectively.

onSuccess and onError

Below is the combined options example.

combined example

You have covered some of the parameters section that you need to pass in useSWR, now moving towards the response section that you are getting from useSWR.

As earlier mentioned above, useSWR returns a total of five values in response. Let’s understand what it is.

Response

  • data: it is the data returned from the request to the given key. If the data is not yet available in that case it is undefined.
  • error: It is an error object if the request failed, or undefined if there is no error.
  • isLoading: It’s a boolean returned value that indicates that data is currently being fetched.
  • isValidating: It’s a boolean returned value that indicates that there is an ongoing request or revalidation is loading.
  • mutate: It’s a function for making changes to the cached data specific to a given cached key only.

You have completed the basics of useSWR, below is the full-scale example.

complete useSWR example

Advanced topics related to useSWR

In this section, you will see some advanced topics related to useSWR that can help you unlock its full potential in your ReactJS or NextJS applications.

Conditional fetching

Sometimes, you need to fetch data that depends on the result of another request. useSWR allows you to conditionally fetch data based on the availability of a key or another piece of data.

conditional fetching

Here fetching posts of a user only after the user’s data is successfully retrieved.

Optimistic UI updates

Optimistic UI updates allow you to update the UI immediately after an action, even before the server confirms the change. useSWR’s mutate function can be used for this purpose.

optimistic ui updates

Caching strategies

You can make changes to the default cache using useSWRConfig hook.

caching strategy

As seen above, you can update the default cache by two methods: first is directly by cache.set() method (commented code line 16) and the second is by mutate function given by useSWRConfig hook.

But you should never update the cache directly. When you update the cache directly (e.g., using cache.set), other components or hooks that rely on that cache might not be aware of the update since it bypasses SWR’s built-in revalidation mechanism. This can lead to inconsistencies in your UI because those components won’t automatically re-render or re-fetch data based on the new cache value which results from your application continuing to display outdated data.

So, to overcome all of this you should always use the mutate function for caching updates. The mutate function not only updates the cache but also triggers revalidation or re-rendering of components that use the affected data. This ensures that all parts of your application remain in sync with the updated data.

Pagination

Implementing pagination using useSWR can be achieved by managing the page state and fetching data for each page.

pagination

Above is an example of infinite loading and clicking the “Load More” button increments the page state, triggering a new fetch for the next set of data. When the data is successfully fetched (onSuccess), it appends the new data to the posts array.

However, this can also be achieved by useSWRInfinite hook, which is specifically designed for handling paginated data fetching, such as infinite scrolling. useSWRInfinite simplifies the process by automatically managing the pagination logic.

useSWRInfinite allows you to fetch data in pages, with each page being loaded sequentially. The key feature of useSWRInfinite is the getKey function, which defines the key for each page based on the page index.

pagination using useSWRInfinite

You might be wondering about pageIndex and previousPageData. Let’s understand this.

PageIndex is a parameter automatically supplied by the useSWRInfinite hook. This is a number representing the current page being fetched. It starts from 0 for the first page, 1 for the second page, and so on. The useSWRInfinite hook internally manages the pagination logic and keeps track of how many pages have been requested so far. Each time you call setSize(size + 1), indicating that you want to fetch the next page, useSWRInfinite increments the pageIndex and then calls the getKey function with the updated pageIndex.

Also, the previousPageData parameter plays an important role in determining whether to continue fetching more data and how to construct the key for the next page. This parameter represents the data returned from the previous page fetch. It allows you to inspect the result of the last request to decide whether to fetch more data or not.

When fetching the first page, previousPageData is null and for subsequent pages, previousPageData contains the data that was fetched for the previous pageIndex.

Conclusion

By mastering useSWR, you unlock a powerful tool for efficient data fetching and state management in your React applications. Whether you’re building complex features like pagination or simply optimizing API calls, useSWR provides the flexibility and performance you need to deliver seamless user experiences.

Happy coding!

FOUND THIS USEFUL? SHARE IT

Leave a Reply

Your email address will not be published. Required fields are marked *