TanStack Query, formerly known as React Query, is a robust data-fetching library for React applications. It simplifies data fetching, state management, and caching.
TanStack Query provides ample options to customize the query. It defines data freshness with options like staleTime and cacheTime, giving control over when data should be refetched.
It automatically fetches data when necessary, such as when a component mounts, the window regains focus, or network status changes based on various options.
Key Customization Options for TanStack Query (React Query)
- staleTime: This option defines how long the data is considered fresh. During this period, the data won’t be refetched.
- cacheTime: It specifies how long unused/inactive query data remains in the cache before being garbage collected.
- refetchOnWindowFocus: It defines if the query should refetch when the window regains focus.
- refetchInterval: It sets a polling interval to automatically and periodically refetch data.
- retry: It controls the retry behavior for failed queries.
- enabled: This option allows conditional fetching based on a boolean value.
- initialData: This option provides initial data to populate the query before the first fetch.
Let’s see these options in practice when creating a web application that we can use to customize the TanStack Query.
Here is the step-by-step guide:
Step 1: Setup a Vite Project with a React template
Open your terminal (like a command line tool) and type the command below to create a React boilerplate:
npm create vite@latest tanstack-query-demo -- --template react cd tanstack-query-demo npm install
Step 2: Install TanStack Query, MUI, and Axios
Install the necessary dependencies mentioned in the title using the command below:
npm install axios @tanstack/react-query @mui/material @emotion/react @emotion/styled
Step 3: Create API functions
We will fetch the data from an API. For this project, we will use this API:
https://jsonplaceholder.typicode.com/posts
Let’s create an “api” folder under the “src” folder.
Under the “api” folder, create a new file called “posts.js”. This file will export an async function that returns the data from an API.
// src/api/posts.js import axios from 'axios'; export const fetchPosts = async () => { const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts'); return data; };
We sent a GET network request to an API that returns the data in JSON format.
Step 4: Create a utility hook
Create a new folder called “hooks” under the “src” folder.
We will create two hooks for this application:
- useQueryWithStatus
- usePosts
The useQueryWithStatus utility hook is designed to simplify and standardize the use of useQuery across your web application. This hook helps reduce redundant code and makes it reusable. It ensures a consistent way of handling query results across different components and defines the professional standard for handling query management.
Create a new file called “useQueryWithStatus.js” and add the code below in it:
// src/hooks/useQueryWithStatus.js import { useQuery } from '@tanstack/react-query'; export const useQueryWithStatus = (queryKey, queryFn, options) => { const { data, error, isLoading } = useQuery(queryKey, queryFn, options); return { data, error, isLoading }; };
Step 5: Create a custom hook with Query customization
Another hook file is usePosts.js, the main Query-related file for our application, where the real customization takes place. This hook file will leverage the useQueryWithStatus utility hook to handle the query logic and apply specific query customization options.
The main purpose of the usePosts.js hook is to fetch a list of posts from the API function.
Here is what the code looks like:
// src/hooks/usePosts.js import { fetchPosts } from '../api/posts'; import { useQueryWithStatus } from './useQueryWithStatus'; // Custom hook for fetching posts with customized query behaviors export const usePosts = () => { return useQueryWithStatus({ queryKey: ['posts'], queryFn: fetchPosts, options: { staleTime: 5 * 60 * 1000, cacheTime: 30 * 60 * 1000, refetchOnWindowFocus: false, refetchInterval: false, retry: 1, // Retry failed requests once }, }); };
In this code, we used the useQueryWithStatus hook and passed the specific queryKey ([‘posts’]), queryFn (fetchPosts), and options to useQueryWithStatus.
You can see that we provided the options object with the following customizable properties:
- staleTime: It defines how much time the data is considered fresh. In this code, the data is deemed fresh to 5 minutes. This means that after 5 minutes, it will try to refetch the updated data otherwise.
- cacheTime: It defines the cache time used by TanStack Query. In this code, we defined the cache for 30 minutes before being garbage collected. This means the data has been cached by TanStack Query.
- refetchOnWindowFocus: If this property is set to false, refetching will be prevented when the window gains focus. When browsing other windows and suddenly returning to your original window based on this property, it will refetch the data. If true, it will refetch; otherwise, it won’t refetch.
- refetchInterval: It defines periodic refetching. We have disabled this property in this hook.
- retry: It defines the count of how many times it will try to refetch. We set it to 1, which means it will try to refetch a single time.
Step 6: Create the Loading and Error Components
Create a new folder called components under the src folder, and inside that folder, create the following two standalone components:
- Loading.jsx: While the data is fetching, this component will be rendered!
- Error.jsx: If something went wrong, it would display this component.
// src/components/Loading.jsx import { CircularProgress, Box } from '@mui/material'; const Loading = () => ( <Box display="flex" justifyContent="center" alignItems="center" height="100vh"> <CircularProgress /> </Box> ); export default Loading;
And the code for Error.jsx:
// src/components/Error.jsx import { Typography, Box } from '@mui/material'; const Error = ({ message }) => ( <Box display="flex" justifyContent="center" alignItems="center" height="100vh"> <Typography color="error">{message}</Typography> </Box> ); export default Error;
Step 7: Create the Post Components
Let’s create two components that display the component data.
Under the components folder, create the following two files:
- PostItem.jsx
- PostList.jsx
The code for PostItem.jsx code is this:
// src/components/PostItem.jsx import { ListItem, ListItemText } from '@mui/material'; const PostItem = ({ post, onClick }) => ( <ListItem onClick={() => onClick(post.id)}> <ListItemText primary={post.title} /> </ListItem> ); export default PostItem;
The code for PostList.jsx code is this:
// src/components/PostList.jsx import { usePosts } from '../hooks/usePosts'; import { Container, List } from '@mui/material'; import PostItem from './PostItem'; import Loading from './Loading'; import Error from './Error'; const PostList = () => { const { data, error, isLoading } = usePosts(); if (isLoading) return <Loading />; if (error) return <Error message={error.message} />; return ( <Container> <List> {data.map(post => ( <PostItem key={post.id} post={post} /> ))} </List> </Container> ); }; export default PostList;
While the data is fetching, this component will display the Loading component. If an error occurs, it will display the error component. If everything works fine, it will display the data from the API in the proper format.
Step 8: Set Up the App and main Components
Update the src/App.jsx file to include the PostList component, which is a container component that holds the data and instantiates the QueryClient.
// src/App.jsx import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import PostList from './components/PostList'; import { Container, CssBaseline } from '@mui/material'; const queryClient = new QueryClient(); const App = () => ( <QueryClientProvider client={queryClient}> <CssBaseline /> <Container> <PostList /> </Container> </QueryClientProvider> ); export default App;
And the updated src/main.jsx file looks like this:
// src/main.jsx import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode> <App /> </React.StrictMode> );
Step 9: Run the project
To run the Vite project, type the following command:
npm run dev
Go to this URL: http://localhost:5173/
You can see the post displayed in the proper format.
That’s all!