React Query is a lightweight library for fetching, caching, and updating asynchronous data in React applications. It is designed to work with a variety of data sources, including REST APIs and GraphQL endpoints.

It is used for managing, caching, and synchronizing asynchronous data in React applications. It makes it easy to work with asynchronous data and API requests in React.

React Query provides a set of hooks that can be used to fetch, cache, and update data in your application. You can use the useQuery hook to execute a query and subscribe to updates, and the useMutation hook to execute mutations (i.e., updates) to the data. It also provides several configuration options to customize the behavior of the library to fit your specific needs.

The latest version of React Query is renamed as TanStack Query and it has support for Solid, Svelte, and Vue as well. However, we will stick to React for this blog.

React Query - How it works?

  • We begin, by defining a query by calling a hook (e.g. useQuery) and passing it a unique key representing the data to be fetched.

  • The hook will return a tuple containing three elements: a status (such as “loading”, “error”, or “success”), the data that was fetched, and a function to refetch the data.

  • If the data is already stored in the cache, it will be returned immediately. Otherwise, React Query will make a request for the data and store it in the cache for future use.

  • The status and data returned by the hook can be used in your React component to render the UI. For instance, you could show a loading spinner while the data is being fetched or display an error message if there was a problem.

  • If the data changes, React Query will automatically update the cache and re-render the component with the new data.

  • In addition, React Query offers several other features, including cache invalidation, deduplication of queries, and the ability to cancel ongoing requests.

Setting up React Query in a React application

To incorporate React Query into a React app, you’ll need to install the library. This can be done by running the following command in the terminal:

npm i @tanstack/react-query

After installation, follow the below steps:

  • Create a client object for making HTTP requests to your API using a library like axios or fetch.

  • Wrap your top-level component with the QueryClientProvider component and pass in the client object from step 1. This will provide the client object to all components in your app.

import { QueryClientProvider, QueryClient } from '@tanstack/react-query';

const client = QueryClient();
const App = () => (
  <QueryClientProvider client={client}>
    {/* Your app goes here */}
  </QueryClientProvider>
);
  • Now that you’ve set up React Query, you can use the useQuery and useMutation hooks to fetch and update data in your React app.

useQuery Hook

  • Queries are used to request data from an asynchronous source, such as a server, by providing a unique key and a function that returns a promise.

  • The useQuery hook allows a query to be subscribed to in a component or custom hook.

  • The query result returned by useQuery contains information about the query’s status, data, and any errors that occurred.

  • Queries can be in a loading, success, or error state and also have a fetchStatus of fetching, paused, or idle.

  • Queries can be refetched manually or automatically and can also be canceled to stop unnecessary data fetching.

Consider the below example :

import { useQuery } from '@tanstack/react-query';

const fetchTodoList = async () => {
    const response = await fetch('/api/todos');
    return response.json();
};
function MyComponent() {
    const { isLoading, isError, data, error } = useQuery({
        queryKey: ['uniqueIdentifier'],
        queryFn: fetchTodoList,
    })
    if (isLoading) {
        return <span>Loading...</span>
    }
    if (isError) {
        return <span>Error: {error.message}</span>
    }
    return (
        <ul>
            {data.map((todo) => (
                <li key={todo.id}>{todo.title}</li>
            ))}
        </ul>
    )
}

The useQuery hook accepts two arguments:-

  • A unique identifier for the query (in this case, a string ‘uniqueIdentifier’)

  • A function that performs the actual query.

When the component containing the useQuery hook is rendered, the hook will execute the query function and return the data, error, and loading state.

The component can then use these values to render the appropriate UI based on the current state of the query.

useMutation Hook

Mutations are used to create/update/delete data or perform server side-effects. For this purpose, React Query exports a useMutation hook.

import { useMutation } from '@tanstack/react-query';

function AddTodo() {
    const mutation = useMutation({
        mutationFn: (newTodo) => {
            return axios.post('/todos', newTodo)
        },
    })
    return (
        <div>
            {mutation.isLoading ? (
                'Adding todo...'
            ) : (
                <>
                    {mutation.isError ? (
                        <div>An error occurred: {mutation.error.message}</div>
                    ) : null}
                    {mutation.isSuccess ? <div>Todo added!</div> : null}
                    <button
                        onClick={() => {
                            mutation.mutate({ id: new Date(), title: 'Do Laundry' })
                        }}
                    >
                        Create Todo
                    </button>
                </>
            )}
        </div>
    )
}

In the above code,

  • The AddTodo function, which is defined in this code, has a button that, when clicked, sends a POST request to an API endpoint to add a to-do item.

  • The useMutation hook takes a function that returns a promise as an argument. useMutation hook returns a function that you can call to execute the mutation.

  • We then use this function as the onClick handler for the button element.

  • When the button is clicked, the addTodo function will be called and the mutation will be executed.

  • The useMutation hook will handle the loading and error state for the mutation automatically.

Query Invalidation

  • Often while working with complex front-end applications, we would encounter stale data, and the need to refresh them.

  • This can be done by invalidating the query.

  • The QueryClient object has an invalidateQueries object which can be used to mark queries as stale and also refetch the same.

The below code will invalidate all the queries in the application.

queryClient.invalidateQueries();

The below code will invalidate every query with a key that starts with todos.

queryClient.invalidateQueries();

Benefits of Using React Query for Data Management

Some of the benefits of using React Query include:

  • Declarative API:

React Query provides a declarative API for managing data and removes the need for managing data fetching and caching manually.

  • Automatic cache invalidation:

React Query handles cache invalidation and removes the need for manually invalidating the cache when data changes.

  • Refetching on the interval:

React Query allows to specify an interval for refetching data to keep the data up-to-date in the application.

  • Concurrent mode support:

React Query works well with React’s concurrent mode, allowing one to fetch data in the background while rendering the UI.

  • Suspense support:

React Query can be used with React’s Suspense feature to manage loading states in your application.

Conclusion

React Query helps simplify the process of managing data in a React application. It makes it easier to build performant and scalable applications.

Happy Coding !