Using React Query for Data Fetching
React Query is a powerful library that simplifies data fetching, caching, synchronization, and background updates in React applications. It handles common scenarios like pagination, infinite scrolling, caching, and retrying failed requests. React Query abstracts many complexities involved in data fetching, making it easier to manage server state in your React application.
Here’s how to use React Query for data fetching in a React app.
1. Installation
To use React Query, you first need to install it.
npm install react-query
You’ll also need to wrap your application in a QueryClientProvider
to provide React Query’s functionality to your components.
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
{/* Your application components */}
</QueryClientProvider>
);
}
export default App;
2. Fetching Data with React Query
React Query provides a useQuery
hook that makes fetching data from APIs incredibly simple.
Basic Example:
import React from 'react';
import { useQuery } from 'react-query';
// Fetch function to get data from an API
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
};
const FetchDataWithReactQuery = () => {
// Using useQuery hook to fetch data
const { data, error, isLoading, isError } = useQuery('fetchData', fetchData);
if (isLoading) {
return <div>Loading...</div>;
}
if (isError) {
return <div>Error: {error.message}</div>;
}
return (
<div>
<h1>Data Fetched:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};
export default FetchDataWithReactQuery;
Explanation:
useQuery
: TheuseQuery
hook is used to fetch data. The first argument'fetchData'
is the query key, which uniquely identifies the data. The second argument is the fetch function that returns a promise (e.g., API call).- Loading state: While the data is being fetched,
isLoading
istrue
, and you can show a loading indicator. - Error handling: If there’s an error,
isError
will betrue
, and the error message will be displayed. - Data: Once the data is fetched successfully, it’s available in the
data
variable.
3. Query Caching and Background Refetching
React Query automatically caches the fetched data and provides ways to keep it up-to-date by refetching in the background.
Example with Background Refetching:
const FetchDataWithRefetch = () => {
const { data, error, isLoading, isError, refetch } = useQuery(
'fetchData',
fetchData,
{
refetchInterval: 5000, // Automatically refetch every 5 seconds
}
);
if (isLoading) {
return <div>Loading...</div>;
}
if (isError) {
return <div>Error: {error.message}</div>;
}
return (
<div>
<h1>Data Fetched:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
<button onClick={refetch}>Refetch Data</button>
</div>
);
};
Explanation:
refetchInterval
: This option tells React Query to refetch the data periodically (every 5 seconds in this case).refetch
: You can manually trigger a refetch by calling therefetch
function.
4. Handling Pagination and Infinite Queries
React Query makes handling pagination and infinite scrolling simple with its useInfiniteQuery
hook.
Example of Infinite Scrolling with useInfiniteQuery
:
import React from 'react';
import { useInfiniteQuery } from 'react-query';
const fetchPageData = async ({ pageParam = 1 }) => {
const response = await fetch(`https://api.example.com/data?page=${pageParam}`);
const data = await response.json();
return data;
};
const InfiniteScrollData = () => {
const {
data,
error,
isLoading,
isError,
fetchNextPage,
hasNextPage,
} = useInfiniteQuery('fetchData', fetchPageData, {
getNextPageParam: (lastPage, allPages) => lastPage.nextPage, // Assumes API provides the next page info
});
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Infinite Scrolling Data</h1>
<ul>
{data.pages.map((page, index) => (
<li key={index}>
{page.items.map(item => (
<div key={item.id}>{item.name}</div>
))}
</li>
))}
</ul>
{hasNextPage && (
<button onClick={() => fetchNextPage()}>Load More</button>
)}
</div>
);
};
export default InfiniteScrollData;
Explanation:
useInfiniteQuery
: This hook is used to fetch data for paginated APIs. ThepageParam
argument specifies the current page, and React Query automatically manages it.getNextPageParam
: This function tells React Query how to determine the next page. It returns the value of the next page from the last page.fetchNextPage
: This function is used to load the next page of data.hasNextPage
: This boolean indicates if there is another page of data available to load.
5. Mutations with React Query
React Query not only handles fetching data but also supports mutations, which are operations that modify server-side data (e.g., creating, updating, or deleting records).
Example of Mutation with React Query:
import React from 'react';
import { useMutation, useQueryClient } from 'react-query';
const createData = async (newData) => {
const response = await fetch('https://api.example.com/data', {
method: 'POST',
body: JSON.stringify(newData),
headers: {
'Content-Type': 'application/json',
},
});
return response.json();
};
const CreateDataWithMutation = () => {
const queryClient = useQueryClient();
const { mutate, isLoading, isError, error } = useMutation(createData, {
onSuccess: () => {
// Invalidate or refetch queries after a successful mutation
queryClient.invalidateQueries('fetchData');
},
});
const handleCreate = () => {
mutate({ name: 'New Data' });
};
if (isLoading) return <div>Creating...</div>;
if (isError) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Create New Data</h1>
<button onClick={handleCreate}>Create</button>
</div>
);
};
export default CreateDataWithMutation;
Explanation:
useMutation
: This hook is used for creating, updating, or deleting data on the server.mutate
: The function used to trigger the mutation.onSuccess
: This callback is executed after the mutation succeeds. It’s often used to invalidate or refetch the query to update the data on the client.invalidateQueries
: This tells React Query to refetch the data associated with a specific query key (e.g.,fetchData
) after a successful mutation.