Optimizing Data Fetching in React with React Query

ยท

7 min read

Introduction

In the world of modern web development, efficient data management is crucial for creating fast, responsive, and scalable applications. React, a popular JavaScript library for building user interfaces, provides developers with various tools for managing data fetching. However, handling complex data fetching requirements can often lead to boilerplate code, performance issues, and a lack of consistency across components.

This is where React Query comes in. React Query is a powerful library that simplifies data fetching in React applications by providing a unified and intuitive API. It offers features such as query caching, automatic background refetching, and optimistic updates, which help developers optimize their applications for speed and efficiency.

In this blog post, we will explore how React Query can be used to optimize data fetching in React applications. We will cover the basics of React Query, its key features, and how it can be integrated into your projects to improve performance and developer experience. Let's dive in!

Traditional data fetching through useState and useEffect

For this article, I am using typicode's posts api, Before diving into tanstack query (react query) first let's discuss the traditional way of data fetching in React applications using useState and useEffect Hooks.

  1. Using states for maintaining data, loading and Error:

    App.tsx

     const [posts, setPosts] = useState<Posts[]>([]);
     const [loading, setLoading] = useState(false);
     const [error, setError] = useState(false);
    

    Since, I am using Typscript, I have a types defined for my data. I have placed it in a separate file.

    utils/types.ts

     export type Posts = {
       id: string;
       title: string;
       body: string;
       userId: string;
     };
    
  2. Using an UseEffect Hook to make an api call

     async function getPosts() {
         setLoading(true);
         await fetch("https://jsonplaceholder.typicode.com/posts")
           .then((response) => response.json())
           .catch((err) => setError(err))
           .then((result) => {
             setPosts(result);
           });
         setLoading(false);
       }
    
       useEffect(() => {
         getPosts();
       }, []);
    
  3. The above two steps will fetch the data from the api endpoint and put the data in our posts state. Now, to render the data on the screen, we will write the JSX

     return (
         <main>
           {loading ? (
             <div>Loading...</div>
           ) : (
             <ul>
               {posts.map((d) => (
                 <li
                   style={{
                     textAlign: "left",
                   }}
                   key={d.id}
                 >
                   {d.title}
                 </li>
               ))}
             </ul>
           )}
         </main>
       );
    

As you can see that to render our simple posts data on our screen, we have to write so much of boilerplate code, maintaining three state variables, a useEffect hook and in JSX, how to conditionally render by writing so much of nested conditions. Of course, in this case I have considered a very simple open source api, but if we more complex api where there many validations then this code only grows in size.

Surely, there must be a better way of data fetching. Yes, there is a way using Tanstack query

Why React Query? and what are its benefits

React Query is a powerful library that simplifies data fetching and state management in React applications. Here are some key reasons why you might consider using React Query:

  1. Simplifies Data Fetching: React Query provides a declarative way to fetch, cache, and update data, reducing the amount of boilerplate code needed for data fetching compared to traditional approaches using useState and useEffect.

  2. Optimized Performance: React Query comes with built-in caching and background refetching mechanisms, which help optimize performance by reducing the number of unnecessary network requests and ensuring that data is always up to date.

  3. Consistent API: React Query provides a consistent API for fetching data, regardless of the data source (REST API, GraphQL, etc.), making it easier to switch between different data sources without having to rewrite your data fetching logic.

  4. Optimistic Updates: React Query allows you to perform optimistic updates, which means that the UI is updated optimistically before the actual data is updated in the background. This provides a smoother user experience, especially in applications with real-time updates.

  5. Error Handling: React Query provides robust error handling mechanisms, allowing you to easily handle and display errors that occur during data fetching.

Overall, React Query offers a more efficient and developer-friendly way to manage data fetching in React applications, making it a valuable tool for building modern web applications.

Converting our data fetching example to React query way

Installation

To convert your data fetching example to use React Query, you first need to install React Query in your project. You can do this using npm or yarn:

npm i @tanstack/react-query
# or
yarn add @tanstack/react-query

Getting started

  1. Setup the Tanstack query provider

    Main.tsx

     import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
    
     const queryClient = new QueryClient();
    
     ReactDOM.createRoot(document.getElementById("root")!).render(
       <React.StrictMode>
         <QueryClientProvider client={queryClient}>
           <App />
         </QueryClientProvider>
       </React.StrictMode>
     );
    
  2. Remove State Variables:

    Since React Query manages the data and loading state internally, you can remove the useState hooks for posts, loading, and error.

  3. Modifying getPosts function:
    Our getPosts function right now uses setting states, so we have to remove that and return the result instead of setting

     async function getPosts() {
         const response = await fetch("https://jsonplaceholder.typicode.com/posts");
         if (!response.ok) {
           throw new Error("error fetching data");
         }
         return response.json();
       }
    
  4. Using Tanstack's useQuery function

     const {data,isLoading,isError} = useQuery({
         queryKey: ["posts"],
         queryFn: getPosts
     })
    

    In this code snippet, useQuery is used with an object configuration instead of separate arguments. Here's a breakdown of each part:

    1. queryKey:

      • The queryKey is an array that serves as a unique identifier for the query.

      • In this case, ["posts"] is used as the queryKey, indicating that this query is fetching data for the "posts" resource.

      • The queryKey is used internally by React Query for caching and invalidation purposes.

    2. queryFn:

      • The queryFn is a function that defines how to fetch the data for the query.

      • In this example, getPosts is passed as the queryFn. This function is responsible for making the actual API call to fetch the posts data.

      • When useQuery is executed, it will call this queryFn function to fetch the data.

    3. Destructuring:

      • The result of useQuery is destructured into three variables: data, isLoading, and isError.

      • data contains the fetched data when it's available.

      • isLoading is a boolean that indicates whether the query is currently loading.

      • isError is a boolean that indicates whether an error occurred while fetching the data.

  5. Rendering the UI (writing the JSX):

    First lets handle the loading and Error state

if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error fetching data</div>;

Now, moving to rendering the data on the screen. This does does not much, the only change is that earlier, we were handling loading and error in the same JSX but now we have to only handle the rendering data part.

    <ul>
        {posts?.map((post) => (
          <li
            style={{
              textAlign: "left",
            }}
            key={post.id}
          >
            {post.title}
          </li>
        ))}
      </ul>

What more React query has to offer?

The above example shows only the basic use of React Query, it is like tip of the iceberg. React Query is a big ocean in itself, and we have just covered the tip of the iceberg. Below are some of the more features React Query has to offer:

  1. Mutations: Discuss how React Query handles data mutations (i.e., updating, deleting, or creating data) using the useMutation hook. Explain how it simplifies the process of sending mutation requests to the server and updating the cache.

  2. Query Invalidation: Explain the concept of query invalidation in React Query, which allows you to manually invalidate queries based on certain conditions (e.g., when a mutation occurs) to ensure that the data remains up to date.

  3. Prefetching: Discuss the prefetchQuery function in React Query, which allows you to prefetch data for a query in the background, improving the perceived performance of your application.

  4. Query Keys: Explain the concept of query keys in React Query, which are used to uniquely identify queries and manage the cache. Discuss best practices for using query keys effectively in your application.

  5. Pagination and Infinite Loading: Discuss how React Query supports pagination and infinite loading for fetching large sets of data efficiently. Explain how to implement pagination and infinite loading using React Query's built-in features.

  6. Error Handling: Provide a detailed explanation of how React Query handles errors during data fetching and mutation operations, including strategies for displaying error messages to users and retrying failed requests

Conclusion

This is first part of React Query library and I will try to cover many more features of React Query in the subsequent Blogs. Stay tuned and subscribe to my news Letter to be the first to get to know when the blog comes out. To read more about Tanstack Query, click here

To get access to the code that I have used above, click here. For any query, you can get in touch with me via LinkedIn and twitter.Leave all your comments and suggestions in the comment section. Let's connect and share more ideas.

Happy coding!

ย