Blog

Overview: TanStack / React Query

Technology

Overview: TanStack / React Query

React Query (recently renamed to TanStack query) is a data-fetching library that makes fetching, caching, synchronizing, and updating server state in your web applications a breeze.

The state management libraries like Redux, Recoil, and Zustand are great for working with client state, they are not so great at working with async or server state. This is because the server state is different.

Server state:

  • Is persisted remotely in a location you do not control or own
  • Requires asynchronous APIs for fetching and updating
  • Implies shared ownership and can be changed by other people without your knowledge
  • Can potentially become “out of date” in your applications if you’re not careful

Once you grasp the nature of the server state in your application, even more challenges will arise as you go, for example:

  • Caching… (possibly the hardest thing to do in programming)
  • Deduping multiple requests for the same data into a single request
  • Updating “out of date” data in the background
  • Performance optimizations like pagination and lazy loading of data
  • Managing memory and garbage collection of server state

Coding examples 

  • Using react query the pain of handling state updation is taken care by the useQuery hook
  • It also reduces the number of lines in a component if the component deals with multiple API calls

Without React Query

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

import axios from 'axios'

const TodoList = () => {

const [todos, setTodos] = useState([]);

const [loading, setLoading] = useState(true);

useEffect(() =>

const fetchTodos = async () => {

try {

const response = await axios.get(

"https://jsonplaceholder.typicode.com/todos"

);

setTodos (response.data);

} catch (error) {

console.error("Error fetching todos:", error);

} finally {

setLoading(false);

}

}

fetchTodos();

},[])

return {...};

export default TodoList;


With React-Query

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

import axios from 'axios'

import {useQuery} from 'react-query';

const fetchTodos = async () => {

return (await axios.get(

"https://jsonplaceholder.typicode.com/todos"

)).data.todos;

}

const TodoList = () => {

const {data:todos=[], isLoading:loading}=useQuery({queryKey:['todos'],queryFn:fetchTodos})

return {...};

export default TodoList

As you can see from the above code using  useQuery hook from react-query we were able to avoid setting the state from the API response into react state and also loading state logic

Apart from the coding optimizations and other loading utilities, the main thing of react query is the caching of API response which can effectively improve the performance of a page

Here in the below picture, it shows how it caches the data under the hood

Apart from this useQuery API there is another hook called useMutation API which is to perform the mutations on the server like we do with HTTP post, put and delete method

The main advantage of react query

  • Caching
  • Pagination and Infinite scrolling
  • Optimistic updates
  • SSR support
  • Background indicators
  • Also Supports other frameworks
    • Upto v3: only react
    • From v4 (rebranded to @tanstack): Vue, Svelte, Solid
    • Currently v5: Experimental Angular

Problems we faced while using react-query

  • Doesn’t support caching of batch requests

Batching is generally we fetch the multiple request data in a single api call, like if we have to fetch the users data with user id 1,2,3 instead of performing 3 api calls we do in one.

The way react-query cache works is that when we perform one API call containing a batch of request IDs it saves the query containing the request id’s in an array structure. 

The next time, we perform another batch that contains request id’s from the previous batch it doesn’t optimize and sends only the request that doesn’t have data for the request id’s

So we solved this problem using the dataloader(from GraphQL community)  on top of react-query

Note: Both react-query and dataloader have caches so we need to disable dataloader’s cache and let react-query manage it all. That way we get de-duplication and caching from react-query and batching from data loader

Conclusion: The react-query is a great tool for caching and other API-related utilities if your website already doesn’t deal with all of the above-related queries.