Development
-
June 26, 2023

How to Master Data Fetching in React with SWR and TypeScript

Efficient data fetching from various APIs is crucial for almost every web application nowadays.

Probably at first, you start fetching data manually using built-in fetch or Axios, but soon you encounter challenges like handling caching, error handling, and stale data. As your application grows, managing state and handling complex data fetching logic becomes more challenging and time-consuming.

To address these issues, SWR was created. SWR stands for “stale-while-revalidate” and it is a powerful data-fetching library for React that provides a simple and elegant way to handle data fetching, caching, and error handling in a performant manner. In addition, it enables us to use TypeScript, a statically typed superset of JavaScript that can provide an extra layer of type safety.

In this article, we will dive deep into mastering data fetching in React using SWR and TypeScript, exploring the core concepts of SWR, and demonstrating how TypeScript can be used in conjunction with it. We will also provide real-world examples and best practices for efficient and type-safe data fetching in your React applications, as well as discuss cutting-edge features like the use of React Suspense for data fetching.

Understanding SWR and How It Works Behind the Scenes

To properly understand what SWR really is, let’s start with an example.

Imagine you're working at a library providing books to people. Sometimes, people ask for the same book multiple times, creating a time-consuming and repetitive task for you. If you had to grab the book from the same place over and over again, you would spend a lot of time doing the same repetitive job, delivering a poor experience to your customers as they would have to wait for you to finish.

To solve this problem, you could create a record of the requested books within a certain period of time, and keep them nearby. This way, when people ask for the same book you will have it readily available to deliver it faster, with less effort than you would if you had to search for the book each time.

In this example, SWR comes to the game as your library assistant, helping you to keep track of which books are being requested frequently and ensuring that you only go to the bookshelves when it’s needed.

We’ll cover in-depth how to use SWR with React in the next section but, for this explanation, the introduction of a few concepts is needed.

To use SWR with React, we need to use a hook called useSWR which returns some variables depending on the state of the fetcher function (in our example this function would represent the action of going to the bookshelves and grabbing the book).

CODE: https://gist.github.com/Alejoboga20/1eb7dcb21e3114b58d1b133f5d75df00.js?file=fetcher.ts

We can define these variables as follow:

  • data: This variable represents the book itself.
  • error: If for some reason you can not find the book, this variable will contain the reasons why.
  • isLoading: This variable will have a boolean value of true when you are on your way to grab a book.
  • isValidating: This variable will be similar to isLoading but it will have the value of true when you are checking your records if you already have the book next to you. From now on, this place next to you will be called cache.
  • key: Represents the book the customer wants. It could be for example the title.

We can see this clearly in the next diagram.

How SWR works

Under the hood, SWR uses a cache to store data that has been previously fetched. In our example, this would be the closest bookshelf.

When a user requests data, SWR first checks the cache to see if it already has the data. If it does, it returns the cached data immediately. If not, it sends a request to the server to fetch the data, and once the data is returned, it stores it in the cache for future use.

As we mentioned earlier, SWR stands for "stale-while-revalidate." This is a fancy way of saying that it allows us to show the user stale data while the app revalidates in the background. This means that if the user requests the same book multiple times, they will see the cached data immediately instead of waiting for a response each time.

Ok, enough talking, show me some code.

How to Use SWR with React and TypeScript

In order to follow this article we will need to create a React project with TypeScript. I am going to skip those steps as there are plenty of tutorials on how to do that and, I am going to assume that we have a project up and running.

Note: Codebase used in this article can be found here. GitHub Repository.

Once we have our project, the first step to using SWR is to install it as a dependency. Along SWR, I’ll be using Axios. To install both as dependencies run this command in your terminal.

CODE: https://gist.github.com/Alejoboga20/1eb7dcb21e3114b58d1b133f5d75df00.js?file=file.bash

Once we have our dependencies installed, we need to create a fetcher function as mentioned earlier.

For this example, PokeApi will be useful as it exposes a huge data set alongside other interesting features.

CODE: https://gist.github.com/Alejoboga20/1eb7dcb21e3114b58d1b133f5d75df00.js?file=index.ts

This function should be passed as the second argument to our useSWR hook. However, in our case, we will use a configuration context to config our SWR instances globally. To do this, we can import from the same library a SWRConfig context and wrap our entire application in it. This way every time we use a useSWR hook in our application it will have the same configuration.

CODE: https://gist.github.com/Alejoboga20/1eb7dcb21e3114b58d1b133f5d75df00.js?file=App.tsx

We can now fetch data in our application. To do so, we'll use the SWR hook that we've already configured inside our App component. To add type safety, we'll use TypeScript Generics to make it even better.

CODE: https://gist.github.com/Alejoboga20/1eb7dcb21e3114b58d1b133f5d75df00.js?file=PokemonGrid.tsx

Using the isLoading indicator, we can display a loading state to give our users immediate feedback while data is being fetched from the servers and, thanks to TypeScript, our fetched data is completely typed with the attributes we need to build our user interface.

This is the final result of our PokemonGrid component.

Now that we have a basic understanding of how to use SWR with React for data fetching, let's move on to more exciting topics.

Next-Level Data Fetching in React: Combining SWR and React Suspense

In React 18, we can use Suspense for data fetching in opinionated frameworks such as Next or Remix. However, it is not yet recommended to integrate it into data frameworks like SWR, as it is an experimental feature whose APIs may change in the future.

You can read more about the future of Suspense here.

While React Suspense is still considered an experimental feature, it has the potential to streamline data fetching in React applications and, by integrating it with SWR, we can achieve even more performant and seamless data fetching in your applications, without worrying about handling loading states.

To start using Suspense we need to enable the option in our configuration context (Note that we’ve included some extra things like the router, this is just to implement extra examples).

CODE: https://gist.github.com/Alejoboga20/1eb7dcb21e3114b58d1b133f5d75df00.js?file=AppWithRouter.tsx

Once we start using Suspense, data returned by our useSWR hooks will always be the fetch response (so there is no need to check if it is loading anymore). But if an error occurred, you need to use an ErrorBoundary to catch it and, while we wait for the data, the component provided under the fallback property is rendered.

With this in place, our PokemonGrid will look like this:

CODE: https://gist.github.com/Alejoboga20/1eb7dcb21e3114b58d1b133f5d75df00.js?file=PokemonGridWithSuspense.tsx

As you can see, we don’t need to handle the loading state anymore because Suspense will Suspend the rendering of our component until the data is ready and will render the fallback component instead.

This way we can handle a nice loading skeleton to give instant feedback to our users while waiting for the data.

This is not finished yet, as we mentioned earlier, if things go wrong we can easily handle the errors using an error boundary to provide a really good user experience.

We already configured our Error Boundary fallback so, in the case of an error our users will see the next screen.

Prefetching Data With SWR Prefetch Feature

So far we have included really good features for our users like providing loading states and handling errors, but we can make it even better if we use another resource SWR provides called prefetching. This allows us to improve the performance of our application by start fetching our data before the users ask for it. In our example, the equivalent would be if you know ahead of time what books your customer wants, so you can grab them before they ask.

To show you how to use this resource, I’ve made our PokemonCard clickable to redirect to a new page displaying the Pokemon Details.

Without prefetching, our users will have to wait for the new data to be fetched. The good thing is we can include an event handler to detect when the user’s mouse is over the PokemonCard and start fetching the data at that moment. This way when the user navigates to the PokemonDetails page, the data will be ready.

To implement this improvement we need to make some tweaks in our card component.

CODE: https://gist.github.com/Alejoboga20/1eb7dcb21e3114b58d1b133f5d75df00.js?file=PokemonCard.tsx

As you can see, we can import preload from SWR to start fetching once the user is hovering over the card.

This improvement leads to a huge performance enhancement and, the best thing is, if you hover over the same Pokemon, the data would not be fetched again as it is already in the cache, all of it is automatically handled by SWR. Wonderful 😎

Use SWR Prefetch Feature to Implement a Data Pagination

I didn’t want to miss the chance to talk about data pagination, because it is a common pattern in web applications intended to work with large datasets.

In React applications, implementing pagination brings some challenges, such as managing loading states, prefetching data, and keeping an updated cache. If you made this through you’d know these are the reasons why we are using SWR in the first place and pagination is not the exception so, let’s jump right into it.

The first step is to include a state for React to render the new content as the page number changes and some functions to modify it. After that, we include two buttons to handle the navigation between pages with a prefetch function being triggered when users hover over them.

CODE: https://gist.github.com/Alejoboga20/1eb7dcb21e3114b58d1b133f5d75df00.js?file=PokemonGridWithPagination.tsx

This is the result. Now you can smoothly navigate through thousands of Pokemon thanks to SWR.

Conclusion

In conclusion, data fetching is an essential aspect of building performant and user-friendly React applications. By using SWR and TypeScript, developers can leverage the power of caching, prefetching, and advanced data fetching techniques to streamline their data fetching code and achieve next-level performance. Additionally, by integrating React Suspense, developers can further enhance their data fetching capabilities and provide a seamless user experience.

I hope this article has provided a comprehensive guide to data fetching in React using SWR and TypeScript, and we encourage readers to continue exploring the full potential of these powerful tools in their own React projects.