React Query is often described as the missing data-fetching library for React, but in more technical terms, it makes fetching, caching, synchronizing and updating server state in your React applications a breeze.
Overview of React Query
React Query is an open-source library designed to streamline data management in React applications. It provides a simple and intuitive way to manage server state, enabling efficient data fetching, caching, synchronization, and updates. By using React Query, you can create responsive, real-time applications with minimal effort.
Key Power
Fetching Data
React Query simplifies data fetching by providing hooks like useQuery
. These hooks make it easy to send HTTP requests to your server and retrieve data. To get started, you can create a query like this:
data
: Contains the fetched data.error
: Holds any potential errors during data retrieval.isLoading
: Indicates whether the request is in progress.
Caching Data
React Query incorporates intelligent caching by default. When you fetch data using useQuery
, it stores the results in a cache for future use. This means that subsequent requests for the same data are lightning fast, as they’re served from the cache.
Synchronizing Data
Synchronizing data with React Query is effortless. When you modify data on the server, React Query provides tools like useMutation
for updating and synchronizing the client’s data automatically. Here’s an example of using useMutation
:
Updating Server State
React Query allows you to seamlessly update the server state. When you trigger a mutation with useMutation
, it sends a request to the server, and upon success, you can update the local cache. This ensures that your client’s data remains in sync with the server.
In addition to these key points, there are some important practices to keep in mind when working with React Query:
- Consistency in Key Names: When defining queries and mutations, use descriptive and consistent keys to avoid confusion.
- Optimistic Updates: Implement optimistic updates to provide a smooth user experience by updating the UI before the server responds.
- Custom Hooks: Create custom hooks for complex data-fetching logic to keep your components clean and maintainable.
- Automatic Refetching: Configure queries to automatically refetch at specified intervals to keep data up to date.
- Error Handling: Implement robust error handling strategies to gracefully handle server and network errors.
Let’s Rock
Effect vs ReactQuery
The Evil behind use effect
Before Going any further let’s take a look at our wonderful friend useEffect
Now, let's address what's not ideal about this code and how React Query can help:
- Data Fetching in Components: In this example, data fetching logic is placed directly inside the component. This can lead to components becoming cluttered and harder to maintain as your application grows.
- Lack of Error Handling: There’s no error handling for the API request. If the request fails, the user won’t receive any feedback or see an error message.
- No Caching: There’s no caching of fetched data. The data will be refetched on every render of this component, which can be inefficient and lead to a poor user experience.
- Missing Loading Indicator: The code doesn’t provide a loading indicator while the data is being fetched, which can leave the user wondering about the state of the request.
The beauty of react query
React Query offers an elegant solution to these issues:
- Separation of Concerns: React Query encourages a clear separation of data fetching and component rendering. You define queries and mutations separately from your components, making your components cleaner and more focused on presentation.
- Built-in Error Handling: React Query has built-in error handling mechanisms, allowing you to gracefully handle errors and provide user-friendly feedback when an API request fails.
- Automatic Caching: React Query handles caching automatically. When you use
useQuery
, it caches data and provides options for cache management. - Loading State Management: React Query simplifies the management of loading states. You can easily check if data is loading and display loading indicators accordingly.
Here’s a revised example using React Query:
- Data fetching logic is abstracted into a
fetchData
function, decoupling it from the component. - Loading and error states are handled explicitly, providing better user feedback.
- Data is automatically cached and updated as needed by React Query.
Fetching Data
React Query does not care of how you fetch your data, weather you are using the Fetch api or Axiox it surely does not give a shit.
For comparisons let’s have a look at codes that uses useEffect and codes that uses react Query
Something we don't want
Something we want !
Now we have access to all the good stuffs,caching,reties…
Configuration of react Query
To configure your react query, you simply go where you instantiated it, and provide a configuration object
Common configurations
React query provides several useful configurations to the tip of your finger !
Patten to write your configuration objects
Config while instantiating our queryObject
Set config per query
retry
Retry is simply the number of retries if a fetching was not successful. maybe at the exact time you were querying things your network went off or few sec. then react query will retry for you.
cacheTime
Stale time
This simply describes for how long data is considered to be fresh !
Auto Refresh of stale data.
React Query automatically refresh stale data under 3 conditions.
When the network is reconnected
When a component is mounted
When the window is refocused
If we don't want this behavior we can always change it!
queryKey
The queryKey
is a unique identifier for the query. It can be a string or an array of strings and objects. When the queryKey
changes, a new query is created.
queryFn
The queryFn
is a function that returns a promise. When the promise resolves, its result will be saved and returned from the useQuery
hook.
staleTime
The staleTime
option determines the time in milliseconds that the cached data remains fresh. After this time, if the query is rendered again, React Query will automatically refetch the data in the background.
cacheTime
The cacheTime
option specifies the time in milliseconds that unused/inactive cache data remains in memory. After this time, the unused cache data will be garbage collected.
retry
The retry
option determines the number of times to retry the query in case of failure. By default, it’s set to 3
.
Remember, React Query provides a lot more configuration options that you can use based on your needs. Refer to the official React Query documentation for more details.
Use Query Hook (useQuery)
Ways to Configure useQuery Hook in React Query
React Query offers multiple ways to configure the useQuery
hook. Here is a comprehensive list of them:
1. Using separate arguments
The most common way to configure useQuery
is by passing the query key, query function, and an options object as separate arguments.
2. Using configuration object
Alternatively, you can pass all configuration options including the query key and query function as an object.
3. Using an inline function for queryFn
If your query function needs to access variables from the component’s scope, you can define it inline.
4. Using an array for queryKey
If you need to pass variables to your query function, you can include them in the query key as an array.
In this case, your query function will receive an object as argument, and you can extract the variables from the queryKey
property.
5. Using default options with QueryClient
If you want to set default options for all queries, you can use the defaultOptions.queries
property when creating the QueryClient
.
6. Using useQueries for multiple queries
If you need to run multiple queries at once, you can use the useQueries
hook. It accepts an array of objects, where each object is a configuration for a query.
Remember, the way you configure your queries depends on your specific use case and personal preferences. You can choose the method that best suits your needs and makes your code clean and maintainable Source 1.
User Query
Using the use query hook, you can get along using the most minimal settings, you would do something like this
Your custom hook
From your component
Pagination with React Query
Pagination is a common technique used to break large datasets into smaller, manageable chunks, improving both performance and user experience. With React Query, implementing pagination is straightforward. In this guide, we will use the useQuery
hook with its configuration provided as an object to fetch paginated data.
Basic Setup
Firstly, we need to initialize a new instance of React Query by importing QueryClient
and QueryClientProvider
from React Query. Then, we wrap the app with QueryClientProvider
Source 0.
Fetching Paginated Data
The useQuery
hook will be used to fetch the paginated data. The page number will be included as part of the queryKey
Source 4.
In this example, fetchUsers
is our function to fetch the paginated data. It receives an object as an argument, and we can extract the page number from the queryKey
property. The keepPreviousData
option is set to true
, which means that the previous page’s data will be kept until the next page’s data is fetched. This prevents the UI from “jumping” between pages.
Implementing Pagination Controls
To navigate between pages, we can add Next and Previous buttons and manage the current page with a state variable.
In this example, the page
state variable is managed with the useState
hook and passed to the UsersList
component. The Previous Page button decreases the page
state variable by 1, but it cannot go below 1. The Next Page button increases the page
state variable by 1. The Previous Page button is disabled when page
is 1, meaning we’re on the first page.
This guide provides a basic implementation of pagination with React Query. Depending on your API and requirements, you might need to adjust this implementation. For example, if your API includes information about the total number of pages, you might want to disable the Next Page button when the user is on the last page. Also, remember that the keepPreviousData
option only keeps the data from the previous page, not all previous pages. If you need to keep all pages’ data, you might want to consider using the useInfiniteQuery
hook instead Source 4.
Mutations with React Query
Mutations in React Query are used for making any kind of server state change including POST, PUT, DELETE requests, etc. Unlike queries, mutations are not cached and do not automatically re-run on window focus or network reconnection. Mutations are performed using the useMutation
hook
Creating a mutation hook !
Basic Usage
We can use this mutation function in our component using the useMutation
hook.
In this example, useMutation
receives the addUser
function as the first argument. The second argument is an options object where we can specify callbacks like onSuccess
, onError
, and onSettled
that are executed based on the mutation’s state.
In the onSuccess
callback, we’re calling queryClient.invalidateQueries('users')
to refetch the users after a new user is added. This ensures that our UI is updated with the latest data after the mutation.
Error and Loading States
Like useQuery
, useMutation
returns an object that includes properties for tracking the mutation’s error and loading states.
In this example, mutation.isLoading
is true
while the mutation is being performed, and mutation.isError
is true
if the mutation fails. mutation.error
contains the error thrown by the mutation function.
Optimistic Updates
Optimistic updates can be used to update the UI immediately before the mutation is completed. This can make your application feel faster and more responsive
In this example, onMutate
is called immediately when the mutate
method is called. We’re using it to update the cache with the new user before the mutation is performed. If the mutation fails, we rollback the update in the onError
callback using the context returned from onMutate
. Finally, we refetch the users in the onSettled
callback, which is called whether the mutation succeeds or fails.
Remember, mutations are more complex than queries because they can change data on the server, so it’s important to handle all possible states to provide a good user experience