# Series: React Query (12 Parts) — Luca Del Puppo > Complete 12-part technical series "React Query" by Luca Del Puppo (https://delpuppo.net/blog/series/react-query/). Ordered chronologically from Part 1 to Part 12. - **Canonical Series URL**: https://delpuppo.net/blog/series/react-query/ - **Hashnode Series Mirror**: https://blog.delpuppo.net/series/react-query - **Blog Index LLMs.txt**: https://delpuppo.net/blog/llms.txt ## Series Curriculum 1. [Part 1: React Query - useQuery](https://delpuppo.net/blog/react-query/react-query-usequery/) (Mar 15, 2023) — Hey Folks, It's time to take a journey on react query. Don't you know it? Perfect, you are in the... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/react-query-usequery/llms.txt)) 2. [Part 2: React Query - useMutation](https://delpuppo.net/blog/react-query/react-query-usemutation/) (Mar 22, 2023) — Hey Folks, It's time to talk about the second core concept in React Query, mutation. What is... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/react-query-usemutation/llms.txt)) 3. [Part 3: React Query - useIsFetching & useIsMutation](https://delpuppo.net/blog/react-query/react-query-useisfetching-useismutation/) (Mar 29, 2023) — Hey folks, Today it is time to talk about two hooks exposed by react query: useIsFetching and... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/react-query-useisfetching-useismutation/llms.txt)) 4. [Part 4: React Query - DevTools](https://delpuppo.net/blog/react-query/react-query-devtools/) (Apr 5, 2023) — Hey Folks, In this post, you'll learn how to debug and check whatever happens in your React Query... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/react-query-devtools/llms.txt)) 5. [Part 5: React Query - Authentication Flow](https://delpuppo.net/blog/react-query/react-query-authentication-flow/) (Apr 12, 2023) — Every application should handle an authentication flow; in this article, you'll learn how to build an... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/react-query-authentication-flow/llms.txt)) 6. [Part 6: React Query - Filter Your Data](https://delpuppo.net/blog/react-query/react-query-filter-your-data/) (Apr 19, 2023) — Hey folks, Did you know that you can filter your data in react query? Noooo! Ok, it's time to learn... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/react-query-filter-your-data/llms.txt)) 7. [Part 7: React Query - Enable Query](https://delpuppo.net/blog/react-query/react-query-enable-query/) (Apr 26, 2023) — Hey folks, Do you know that you can enable or disable a query in react query? Noooo! Ok, it's time to... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/react-query-enable-query/llms.txt)) 8. [Part 8: React Query - Keys & Prefetching](https://delpuppo.net/blog/react-query/react-query-keys-prefetching/) (May 3, 2023) — Hey Folks, Today I want to share two important things if you are using ReactQuery. The first is how... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/react-query-keys-prefetching/llms.txt)) 9. [Part 9: Improve user experience with optimistic update](https://delpuppo.net/blog/react-query/improve-user-experience-with-optimistic-update/) (May 11, 2023) — Hey Folks, Sometimes to improve the user experience you can decide to bet on the success of your... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/improve-user-experience-with-optimistic-update/llms.txt)) 10. [Part 10: React Query - Abort Request](https://delpuppo.net/blog/react-query/react-query-abort-request/) (May 17, 2023) — Hey Folks, Today it's time to learn how you can abort an ongoing request with ReactQuery. Before... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/react-query-abort-request/llms.txt)) 11. [Part 11: React Query - Paginated List](https://delpuppo.net/blog/react-query/react-query-paginated-list/) (Jun 1, 2023) — Hey Folks, Today it's time to learn how you can paginate a list with ReactQuery. It's common to... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/react-query-paginated-list/llms.txt)) 12. [Part 12: React Query - Infinite Queries](https://delpuppo.net/blog/react-query/react-query-infinite-queries/) (Jun 7, 2023) — Hey Folks, Today it's time to learn how you can build an infinite query with React Query. To build... ([Article LLMs.txt](https://delpuppo.net/blog/react-query/react-query-infinite-queries/llms.txt)) --- ## Full Series Content ### Part 1 of 12: React Query - useQuery - **URL**: https://delpuppo.net/blog/react-query/react-query-usequery/ - **Published**: Mar 15, 2023 - **Tags**: react, reactquery, reacthooks Hey Folks, It's time to take a journey on [react query](https://tanstack.com/query/latest). Don't you know it? Perfect, you are in the right place 😁 ## Introduction What is React Query? React query is an npm library created by [@TannerLinsley](https://twitter.com/tannerlinsley). It's a state manager for react that simplifies many tasks like handling the HTTP request state, saving data in the client to prevent many requests, sharing data in your application using hooks and so on. You'll discover more in this series about it, learn how to use it, and appreciate its simplicity in your react applications. ## useQuery The first core concept is useQuery. Through it, you can retrieve data from a source and handle all the states of this request in a very simple way. Let's see an example ```ts import { useQuery } from '@tanstack/react-query'; const fetchTodos = async (): Promise => { const response = await fetch('api/tasks'); if (!response.ok) { throw new ResponseError('Failed to fetch todos', response); } return await response.json(); }; export const useTodos = (): UseTodos => { const { data: todos = [], isLoading, isFetching, error, } = useQuery(['todos'], fetchTodos, { refetchOnWindowFocus: false, retry: 2, }); ... }; ``` In this example, you can see the essentials of useQuery. UseQuery is a react hook; it takes 3 params: - The query key - The query function - The configurations Let's start with the first one. The query key is a key used by react query to identify your queries; through the key, react query is able to store the result and use it in different parts of the application. The key is used to identify the query, and with it you can also reset the query or change the value by code using the React Query Client. The query function is the method used to retrieve data from a source (rest, graphQL, etc. etc.). It's simple; a function that returns a kind of data could be a simple function or, in most cases, a promise. Configurations, ok these are simple :) There are many possible options to run the query in different ways (amount of retries, when refreshing the data, how to cache the data and so on..) The result of this hook has three important objects instead - data; this object contains the result of your query function. Please, pay attention that the data could be undefined too; this is because on the first call when the request is pending, data is not yet presented - isLoading; this flag indicates when react query is loading data. There is also the isFetching flag, which is important if you are creating infinite scrolling. The isFetching flag indicates that there is a request pending, which is perfect if the app is asking for the next info - error: this object contains the error if your request has a problem; using it, you can get the error and create a pretty info message for the user Ok, you have an idea of how useQuery works and what its potentialities are, but if you are more interested in it, you can watch my video to learn more about it. > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=q2r2yoq5zaQ](https://www.youtube.com/watch?v=q2r2yoq5zaQ) ok, that's all! I'll get back to you with another react query feature soon. I hope you enjoyed this content. Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/01-queries) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Part 2 of 12: React Query - useMutation - **URL**: https://delpuppo.net/blog/react-query/react-query-usemutation/ - **Published**: Mar 22, 2023 - **Tags**: react, reactquery, reacthooks Hey Folks, It's time to talk about the second core concept in React Query, mutation. ## What is it? Mutations are actions that a user can do in your application. You can imagine a mutation as an action to change or create something. To understand better in the code what a mutation is, let's start with a code snippet ```ts import { useMutation } from '@tanstack/react-query'; const postTodo = async (text: Todo['text']): Promise => { const response = await fetch('api/tasks', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ text }), }); if (!response.ok) { throw new ResponseError('Failed to insert new todo', response); } return await response.json(); }; export const useAddTodo = (): UseAddTodo => { const { mutate: addTodo, isLoading, error } = useMutation(postTodo, { onSuccess: () => { // Success actions }, onError: (error) => { // Error actions }, }); return { addTodo, }; }; ``` As you can see, the mutation is a simple hook with two params: - The function to handle the request - The options to handle, in this case, the success and the error hooks, but also to configure the mutation (retry, retry delay and so on). The result has three main objects: - mutate: this is the action to run the mutation in your code - isLoading: this flag indicates if the mutation is ongoing - error: this indicates the errors in case there is an error in the request Using mutations in your React applications, you can handle all those actions to mutate the data and simplify the management of the states of these requests. Another important concept to know when you work with mutation is the [QueryClient](https://tanstack.com/query/v4/docs/react/reference/useQueryClient). Using the QueryClient, you can invalidate a query already provided and say to react query to ask again for the data because you are sure that those data are not yet valid after the mutation. To do that, you have to use the `useQueryClient` hook to retrieve the queryClient and using the `invalidateQueries` method, you can invalidate the react query cache for a specific query or multiple queries. Here you can find an example ```ts import { useMutation, useQueryClient } from '@tanstack/react-query'; import { QUERY_KEY } from '../../../../constants/queryKeys'; export const useAddTodo = (): UseAddTodo => { const client = useQueryClient(); const { mutate: addTodo } = useMutation(postTodo, { onSuccess: () => { client.invalidateQueries([QUERY_KEY.todos]); }, }); ... }; ``` Ok, I think you have an idea of how useMutation and useQueryClient work, but if you want to dive into them, don't forget to see my Youtube video. [https://www.youtube.com/watch?v=Cpxoxj9sTXA](https://www.youtube.com/watch?v=Cpxoxj9sTXA) Ok, That's all! I hope you enjoyed this content, and I'll get back to you soon with another concept of React Query. Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/02-mutations) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Part 3 of 12: React Query - useIsFetching & useIsMutation - **URL**: https://delpuppo.net/blog/react-query/react-query-useisfetching-useismutation/ - **Published**: Mar 29, 2023 - **Tags**: react, reactquery, reacthooks Hey folks, Today it is time to talk about two hooks exposed by react query: useIsFetching and useIsMutation. Each of these hooks could be used to understand if there is a fetching request or if there is a mutation request ongoing in the application. They are useful if we need to create a global loader that appears when there are one or more requests on going. But how can you use them? Let's start with the useIsFetching. ```ts import { useIsFetching } from '@tanstack/react-query'; export default function Loader() { const isFetching = useIsFetching(); if (!isFetching) return null; return <>Fetching... } ``` As you can see, the syntax is pretty simple. You can import the hook from the library and you can use the hook in your components. The hook returns only a boolean value that indicates if there are one or more fetching requests in the application. Then with this data, you can decide if you have to show a loader or not. Easy peasy! Now it's time to move to the useIsMutation hook. This hook is similar to the previous one, the only different concept is that this hook handles the mutation requests. Let's see an example! ```ts import { useIsMutating } from '@tanstack/react-query'; export default function Loader() { const isMutating = useIsMutating(); if (!isMutating) return null; return <>Mutating... } ``` As you can notice, the syntax is the same as the previous one, the only difference stands in the name of the hook and in its concept. If you want to find more about these two hooks see my Youtube video about them. > 🎬 **Watch / View Embedded Resource:** [https://youtu.be/dWuUjDNB6Yc](https://youtu.be/dWuUjDNB6Yc) Ok, I think you have an idea of how these two hooks work. I hope you enjoyed this content. See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/03-isfetching-and-mutating) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Part 4 of 12: React Query - DevTools - **URL**: https://delpuppo.net/blog/react-query/react-query-devtools/ - **Published**: Apr 5, 2023 - **Tags**: react, reactquery, reacthooks Hey Folks, In this post, you'll learn how to debug and check whatever happens in your React Query application. It's normal when you begin to learn or use a tool; check the tools around it to understand the developer experience so you can decide whether to continue with it. The React query team knows that and has decided to build a tool to help the developer that wants to work with React Query. This tool is called `react-query-devtools` and you can install it in a simple step. Open your terminal and type ```bash $ npm i @tanstack/react-query-devtools ``` Now, in your project, you can use it and get all the info required to debug your application. This tool is simple to use. In your application, you must import it and render it where you render the `ReactQueryProvider`. ```ts import { QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import React from "react"; import { queryClient } from './react-query/client'; import Router from './Router'; function App() { return ( ... ); } export default App; ``` Easy peasy, no? With ReactQueryDevtools, you don't have to pay attention to the environment to render or not the component because it provides it by default. It renders the component only if the condition `process.env.NODE_ENV === 'development'` is true. You can customize the component or force its render also in production mode if you want. To find out more about these topics, you can check the [documentation](https://tanstack.com/query/latest/docs/react/devtools). The benefit of using this component in your application is that it allows seeing what happens in ReactQuery at runtime. You can check the data saved in the state, how many application parts use the different queries, etc. You can also reset the state or remove part of it to re-fetch the data. Yeap, it exposes many good features to debug and check your React Query application, and it is a good tool for every developer that works with React Query. Here you can find an example of ReactQueryDevtool at work ![Devtool view](https://cdn.hashnode.com/res/hashnode/image/upload/v1680621083189/ae19ed79-87ac-4be6-8099-58c8e4d606c7.png) If you want to find out more, watch my Youtube video about React Query Devtool to see it in action. > 🎬 **Watch / View Embedded Resource:** [https://youtu.be/qXycRUetOX4](https://youtu.be/qXycRUetOX4) Ok, that's all! I think you have an idea of what React Query Devtools is and how you can integrate it into your application! I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/04-devtools) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Part 5 of 12: React Query - Authentication Flow - **URL**: https://delpuppo.net/blog/react-query/react-query-authentication-flow/ - **Published**: Apr 12, 2023 - **Tags**: react, reactquery, reacthooks Every application should handle an authentication flow; in this article, you'll learn how to build an authentication flow in your React Application with React Query. ## Sign Up The first step to build an authentication flow is the sign-up action. As you have already learned in this series, you should build a mutation to do this action. A possible solution could be this ```ts async function signUp(email: string, password: string): Promise { const response = await fetch('/api/auth/signup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }) if (!response.ok) throw new ResponseError('Failed on sign up request', response); return await response.json(); } type IUseSignUp = UseMutateFunction export function useSignUp(): IUseSignUp { const queryClient = useQueryClient(); const navigate = useNavigate(); const { enqueueSnackbar } = useSnackbar(); const { mutate: signUpMutation } = useMutation( ({ email, password }) => signUp(email, password), { onSuccess: (data) => { // TODO: save the user in the state navigate('/'); }, onError: (error) => { enqueueSnackbar('Ops.. Error on sign up. Try again!', { variant: 'error' }); } }); return signUpMutation } ``` By creating a mutation like that, you build the signUp in a very simple and clear way. Now using the `useSignUp` hook, you can get the mutation and call the signUp request to create a new user in your system. As you can notice, the code is pretty simple; the `signUp` method calls the API to post the new user's data and return the user data saved in the database. Then using the `useMutation` hook, you can build the mutation to handle the signUp action. If everything goes ok, the `onSuccess` hook calls the navigation to the home page; otherwise, the `onError` hook shows a toast with an error. In the code, there is a TODO that indicates something missing; we'll get back to this line in the future of this post. ## Sign In The second step to build if you are building an authentication flow is SignIn. In this case, SignIn is pretty similar to SignUp; the only things that change are the endpoint and the scope of the hook. So the code can be this ```ts async function signIn(email: string, password: string): Promise { const response = await fetch('/api/auth/signin', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }) if (!response.ok) throw new ResponseError('Failed on sign in request', response); return await response.json(); } type IUseSignIn = UseMutateFunction export function useSignIn(): IUseSignIn { const queryClient = useQueryClient(); const navigate = useNavigate(); const { enqueueSnackbar } = useSnackbar(); const { mutate: signInMutation } = useMutation( ({ email, password }) => signIn(email, password), { onSuccess: (data) => { // TODO: save the user in the state navigate('/'); }, onError: (error) => { enqueueSnackbar('Ops.. Error on sign in. Try again!', { variant: 'error' }); } }); return signInMutation } ``` I don't want to spend much time describing this hook because it is very similar to the SignUp but only with the references for the SignIn. Also in this case, there is a TODO that we'll remove in the future of the post. ## The user The core part of an authentication flow is where you save the user in the state. To do that, in this case, the best way is to create a new hook called `useUser` which is the owner of the user data. The `useUser` hook must have the user's data, and it has to save the user's data in the local storage and retrieve them when the user refreshes the page or gets back in the future. Let's start with the code that handles the local storage. Typically, this code is created with small functions with a specific goal like the next. ```ts import { User } from './useUser'; const USER_LOCAL_STORAGE_KEY = 'TODO_LIST-USER'; export function saveUser(user: User): void { localStorage.setItem(USER_LOCAL_STORAGE_KEY, JSON.stringify(user)); } export function getUser(): User | undefined { const user = localStorage.getItem(USER_LOCAL_STORAGE_KEY); return user ? JSON.parse(user) : undefined; } export function removeUser(): void { localStorage.removeItem(USER_LOCAL_STORAGE_KEY); } ``` In this way, you can create a small module that handles all the local storage functions for the user. Now it's time to see how you can build the `useUser` hook. Let's start with the code ```ts async function getUser(user: User | null | undefined): Promise { if (!user) return null; const response = await fetch(`/api/users/${user.user.id}`, { headers: { Authorization: `Bearer ${user.accessToken}` } }) if (!response.ok) throw new ResponseError('Failed on get user request', response); return await response.json(); } export interface User { accessToken: string; user: { email: string; id: number; } } interface IUseUser { user: User | null; } export function useUser(): IUseUser { const { data: user } = useQuery( [QUERY_KEY.user], async (): Promise => getUser(user), { refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, initialData: userLocalStorage.getUser, onError: () => { userLocalStorage.removeUser(); } }); useEffect(() => { if (!user) userLocalStorage.removeUser(); else userLocalStorage.saveUser(user); }, [user]); return { user: user ?? null, } } ``` The `getUser` function is simple; it provides the HTTP request to get the user info; if the user is null, return null otherwise, it calls the HTTP endpoint. The `useQuery` hook is similar to the others seen before, but there are two new configurations to understand. - **refetchOnMount** : this option is important to prevent the hook reloads the data each time it is used - **initialData** : this option is used to load the data from the local storage; the initialData accepts a function that returns the initial value; if the initial value is defined, react query uses this value to refresh the data. Now you have all the blocks of the authentication flow, but it's time to link `useSignUp` and `useSignIn` with the `useUser` hook. Using the [QueryClient](https://tanstack.com/query/v4/docs/react/reference/QueryClient) you can set the data of a specific query by the [setQueryData](https://tanstack.com/query/v4/docs/react/reference/QueryClient#queryclientsetquerydata) function. So the previous TODOs comments change in this way ```ts export function useSignUp(): IUseSignUp { const queryClient = useQueryClient(); const navigate = useNavigate(); const { enqueueSnackbar } = useSnackbar(); const { mutate: signUpMutation } = useMutation( ({ email, password }) => signUp(email, password), { onSuccess: (data) => { queryClient.setQueryData([QUERY_KEY.user], data); navigate('/'); }, onError: (error) => { enqueueSnackbar('Ops.. Error on sign up. Try again!', { variant: 'error' }); } }); return signUpMutation } export function useSignIn(): IUseSignIn { const queryClient = useQueryClient(); const navigate = useNavigate(); const { enqueueSnackbar } = useSnackbar(); const { mutate: signInMutation } = useMutation( ({ email, password }) => signIn(email, password), { onSuccess: (data) => { queryClient.setQueryData([QUERY_KEY.user], data); navigate('/'); }, onError: (error) => { enqueueSnackbar('Ops.. Error on sign in. Try again!', { variant: 'error' }); } }); return signInMutation } ``` With two simple lines of code, you can set the user in the `useUser` state because the key used to set the query data is the same as the `useUser`. Then, with an `useEffect` in the `useUser` hook, you can remove or set the user data in the local storage when the user changes ```ts export function useUser(): IUseUser { const { data: user } = useQuery( [QUERY_KEY.user], async (): Promise => getUser(user), { refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, initialData: userLocalStorage.getUser, onError: () => { userLocalStorage.removeUser(); } }); useEffect(() => { if (!user) userLocalStorage.removeUser(); else userLocalStorage.saveUser(user); }, [user]); return { user: user ?? null, } } ``` To complete the authentication flow, the only missing thing is the logout. You can build it with a custom hook called `useSignOut`; its implementation is straightforward and could be done in this way ```ts import { useQueryClient } from '@tanstack/react-query'; import { useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { QUERY_KEY } from '../constants/queryKeys'; type IUseSignOut = () => void export function useSignOut(): IUseSignOut { const queryClient = useQueryClient(); const navigate = useNavigate(); const onSignOut = useCallback(() => { queryClient.setQueryData([QUERY_KEY.user], null); navigate('/auth/sign-in'); }, [navigate, queryClient]) return onSignOut } ``` As you can notice, the hook returns a simple function that clears the value in the user state and navigates to the `sign-in` page. Ok, perfect. Now you have all the notions of building an authentication flow with React Query, but If you want to find out more, watch my youtube video about authentication with React Query > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=JNoiAumC94g](https://www.youtube.com/watch?v=JNoiAumC94g) Ok, that's all from authentication. I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/05-auth) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Part 6 of 12: React Query - Filter Your Data - **URL**: https://delpuppo.net/blog/react-query/react-query-filter-your-data/ - **Published**: Apr 19, 2023 - **Tags**: react, reactquery, reacthooks Hey folks, Did you know that you can filter your data in react query? Noooo! Ok, it's time to learn it! To filter the data in your `useQuery` hook, you have to handle the `select` option. This option accepts a function that is used to filter the data. The function has one parameter, and this parameter contains the result of the query; the scope of the select function is to manipulate the query's result and returns a new result that respects some rules. Let's take a look at this example ```ts import { useQuery } from '@tanstack/react-query'; import { Dispatch, SetStateAction, useCallback, useState } from 'react'; import { QUERY_KEY } from 'src/app/constants/queryKeys'; import { Todo } from 'src/app/models'; import { ResponseError } from '../../../../utils/Errors/ResponseError'; const fetchTodos = async (): Promise => { const response = await fetch('api/tasks'); if (!response.ok) { throw new ResponseError('Failed to fetch todos', response); } return await response.json(); }; interface UseTodos { todos: Todo[]; isLoading: boolean; isFetching: boolean; error?: string; setUserFilter: Dispatch>; } function mapError(error: unknown | undefined): undefined | string { if (!error) return undefined; if (error instanceof ResponseError) return error.response.statusText; if (error instanceof Error) return error.message; return 'Unknown error'; } export const useTodos = (): UseTodos => { const [userFilter, setUserFilter] = useState(null); const filterTodoByAssignee = useCallback( (todos: Todo[]) => { if (!userFilter) return todos; return todos.filter((todo) => todo.assigneeId === userFilter); }, [userFilter] ); const { data: todos = [], isLoading, isFetching, error, } = useQuery([QUERY_KEY.todos], fetchTodos, { refetchOnWindowFocus: false, retry: 2, select: filterTodoByAssignee, }); return { todos, isLoading, isFetching, error: mapError(error), setUserFilter, }; }; ``` As you can notice, this example has some key points to analyse. First, the `filterTodoByAssignee` function is the select function passed to the `useQuery` hook. This function checks the `userFilter` state, and if it is empty, the function returns the entire list; else, it filters the result and returns only the todos that have the `assigneeId` that matches with the `userFilter` value. It's important, in this case, to use the `useCallback` hook and pass to it the right dependencies to prevent a decrease in the performance of the application. Then another important thing is the `setUserFilter` function that it's used to set the `userFilter` value. This function is exposed by the custom hook to permit the users to change the filter. And that's all; in these two simple steps, you have a `select` function to filter the dataset got from the query. You can use this solution also to create a client-side pagination in your react query application (for the server-side pagination, there will be a post in the future) If you want to find out more, check my youtube video about this topic > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=H4l1QAA4Dxw](https://www.youtube.com/watch?v=H4l1QAA4Dxw) Ok, that's all from the select option! I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/06-query-filters) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Part 7 of 12: React Query - Enable Query - **URL**: https://delpuppo.net/blog/react-query/react-query-enable-query/ - **Published**: Apr 26, 2023 - **Tags**: react, reactquery, reacthooks Hey folks, Do you know that you can enable or disable a query in react query? Noooo! Ok, it's time to learn it! Some specific hooks must be enabled only if there are some data in the application; for instance, some queries must run only if the users are logged in. To solve this problem, the `useQuery` hooks accept an option called `enabled` that accepts a boolean to determine if a condition is true or false. Let's take a look at this example ```ts import { useQuery } from '@tanstack/react-query'; import { QUERY_KEY } from 'src/app/constants/queryKeys'; import { Todo } from 'src/app/models'; import { User, useUser } from '../../../../auth/useUser'; import { ResponseError } from '../../../../utils/Errors/ResponseError'; const fetchTodos = async (user: User): Promise => { const response = await fetch(`api/tasks?assigneeId=${user.user.id}`, { headers: { Authorization: `Bearer ${user.accessToken}`, }, }); if (!response.ok) { throw new ResponseError('Failed to fetch my todos', response); } return await response.json(); }; interface UseMyTodos { todos: Todo[]; error?: string; } function mapError(error: unknown | undefined): undefined | string { if (!error) return undefined; if (error instanceof ResponseError) return error.response.statusText; if (error instanceof Error) return error.message; return 'Unknown error'; } export const useMyTodos = (): UseMyTodos => { const { user } = useUser(); const { data: todos = [], error } = useQuery( [QUERY_KEY.myTodos], () => fetchTodos(user!), { refetchOnWindowFocus: false, retry: 2, enabled: !!user, } ); return { todos, error: mapError(error), }; }; ``` As you can notice, in this case, a custom hook calls the API and gets all the todos assigned to the current users. The key point of this example stands in the enabled option. As you can see, this `useQuery` is called only if there is a user; else, the query is stopped. This feature permits developers to be sure to call a specific API only if a condition is satisfied, preventing a bad API request. This feature is pretty simple but very powerful, as you can imagine! To find out more about the `enabled` option take a look at my youtube video about it > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=5JSD_b2slJs](https://www.youtube.com/watch?v=5JSD_b2slJs) Ok, that's all from the enabled option. I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/07-enable-query) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Part 8 of 12: React Query - Keys & Prefetching - **URL**: https://delpuppo.net/blog/react-query/react-query-keys-prefetching/ - **Published**: May 3, 2023 - **Tags**: react, reactquery, reacthooks Hey Folks, Today I want to share two important things if you are using ReactQuery. The first is how the keys are composed, and the second is how to prefetch data to reduce the user's wasted time. So let's start 🚀 ## Keys As you have already learned in this series, React query uses the keys to save the data of a specific API. And these keys must be unique for each specific API. When you create a `useQuery` hook, the first parameter is the query's key, which is an array. This array is used to compose the key, and it is in charge of you creating the key in the right way for your case. As said before, the key is an array, and in this way, you can create a key composed of different data. So, for instance, you can have a key for the list of the todos and a key for the detail of a specific todo (`["todos"]` and `["todo", 2]`.) As you can notice, you can put in the key whatever you want, string number or boolean; it is completely on your side; the important thing you must remember every time is that each key must be unique. This concept, and the key you choose, are also important if you want to prefetch the data because you must use the same key to save the data prefetched in the react query state. ## Prefetching In some scenarios, loading the data before the user asks for it could be interesting. This improves the user experience and reduces the waste time for them. But how can you do that with react query? First, you need to use the `QueryClient` object that exposes the `prefetchQuery` method. This method accepts two parameters: the query's key and the fetch request. In this way, you can handle a simple but powerful prefetching flow with few lines of code. Let's take a look at this example ```ts export const useTodos = (): UseTodos => { const client = useQueryClient(); const [userFilter, setUserFilter] = useState(null); const filterTodoByAssignee = useCallback( (todos: Todo[]) => { if (!userFilter) return todos; return todos.filter((todo) => todo.assigneeId === userFilter); }, [userFilter] ); const { data: todos = [], isLoading, isFetching, error, } = useQuery([QUERY_KEY.todos], fetchTodos, { refetchOnWindowFocus: false, retry: 2, select: filterTodoByAssignee, onSuccess: (data) => { data.forEach((todo) => { client.prefetchQuery([QUERY_KEY.todos, id], () => fetchTodo(id)); }); }, }); return { todos, isLoading, isFetching, error: mapError(error), setUserFilter, }; }; ``` As you can notice, in this example in the `onSuccess` hook, the prefetch feature starts to load the data of every todo in the result of the list. That allows React Query to save the data in the storage and reuse them in the future when the application asks for that data. For instance, in a todo detail hook like this ```ts const fetchTodo = async (id: Todo['id']): Promise => { const response = await fetch(`api/tasks/${id}`); if (!response.ok) { throw new ResponseError(`Failed to fetch todo with id ${id}`, response); } return await response.json(); }; export const useGetTodoById = (id: Todo['id']): UseTodo => { const { data: todo = null, isLoading, error, } = useQuery([QUERY_KEY.todos, id], () => fetchTodo(id), { refetchOnMount: false, refetchOnWindowFocus: false, retry: 2, }); return { todo, isLoading, error: mapError(error), }; }; ``` As you can see, the key of this `useQuery` is the same as the key in the prefetch example, and in this way, ReactQuery is able to understand if the data are already loaded or not. As you can imagine, with this simple feature, you can improve the quality of your application and maybe prefetch some data that could take too long to load. Ok, I think you have a great idea of how key and prefetching work in React Query. To learn more about them, check out also my Youtube video available here 👇 > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=svvx-pl_hTE](https://www.youtube.com/watch?v=svvx-pl_hTE) I think thats all from this article; I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/08-query-key-and-prefetch-data) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Part 9 of 12: Improve user experience with optimistic update - **URL**: https://delpuppo.net/blog/react-query/improve-user-experience-with-optimistic-update/ - **Published**: May 11, 2023 - **Tags**: react, reactquery, reacthooks Hey Folks, Sometimes to improve the user experience you can decide to bet on the success of your code, so you can assume that the code will go in the right way to make your application faster in the eyes of your users. This approach is called Optimistic Update and can be handled in some lines of code using react query with the `useMutation` hook. Yes, this approach is like a bet with the code but it's also important to keep in mind to handle the errors in case of failure. To handle the optimistic update you have to handle an `useMutation` hook in your codebase. This hook exposes three events to handle to perform the optimistic update: `onMutate`, `onSuccess` and the `onError`. The `onMutate` event is called immediately when your code calls the `useMutation` hook. This event is used to create a snapshot of the current state before moving forward and updating the state with the new values. It's important to save the previous state because it permits you in case of failure to restore it in the future if needed. The `onSuccess` event is called in case of success of the mutation. If your code jumps in this event, you are safe and everything has gone in the right way. The `onError` event is called in case of failure of the mutation. If your code is inside of this event unfortunately something went wrong. In this case, you must handle the restoration of the state to the previous one and notify the user that something went wrong. To help you to understand better the case let's see an example ```ts import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useSnackbar } from 'notistack'; const editTodoRequest = async (todo: Todo): Promise => { await new Promise((resolve) => setTimeout(resolve, 3000)); const response = await fetch(`api/tasks/${todo.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(todo), }); if (!response.ok) { throw new ResponseError(`Failed to edit todo with id ${todo.id}`, response); } return await response.json(); }; type UseEditTodo = (todo: Todo) => void; export const useEditTodo = (): UseEditTodo => { const { enqueueSnackbar, closeSnackbar } = useSnackbar(); const client = useQueryClient(); const { mutate: editTodo } = useMutation< Todo, unknown, Todo, { oldTodos?: Todo[]; oldTodo?: Todo; messageKey: string | number; } >(editTodoRequest, { onMutate: (todo) => { const messageKey = enqueueSnackbar('Todo edited', { variant: 'success', }); const oldTodos = client.getQueryData([QUERY_KEY.todos]); const oldTodo = client.getQueryData([QUERY_KEY.todos, todo.id]); client.setQueryData([QUERY_KEY.todos, todo.id], todo); client.setQueryData([QUERY_KEY.todos], (oldTodos) => oldTodos?.map((oldTodo) => (oldTodo.id === todo.id ? todo : oldTodo)) ); return { oldTodos, oldTodo, messageKey, }; }, onSuccess: () => { client.invalidateQueries([QUERY_KEY.todos]); }, onError: (error, todo, ctx) => { if (!ctx) return; const { oldTodos, oldTodo, messageKey } = ctx; closeSnackbar(messageKey); const errorMessage = mapError(error); enqueueSnackbar( `Ops! There was an error on editing todo: ${errorMessage}`, { variant: 'error', } ); client.setQueryData([QUERY_KEY.todos, todo.id], oldTodo); client.setQueryData([QUERY_KEY.todos], oldTodos); }, }); return editTodo; }; ``` As you can notice, in the mutate event, the code creates a toast for the user, which indicates that everything has gone in the right way and after that, the code takes the current state, returns it and update the state with the new values. The return is important because it permits getting this data in the `onError` event in case of failure. The `onError` event, as you can notice, handles the restoration of the state, removes the success toast and shows a new one with the error message. This part is crucial if you want to handle an optimistic update because it gives feedback to the user in case of failure. Last but not least, you can also handle the `onSuccess` event if you want to invalidate a query after the mutation or for some other motivation. The `onSuccess` is not required always but depends on your case. Ok, I think now you have an idea of how the optimistic update works in react query, but if you want to dive into it check out my youtube video too. > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=IwkhNxIyyzA](https://www.youtube.com/watch?v=IwkhNxIyyzA) I think thats all from this article; I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/09-optimistic-update) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Part 10 of 12: React Query - Abort Request - **URL**: https://delpuppo.net/blog/react-query/react-query-abort-request/ - **Published**: May 17, 2023 - **Tags**: react, reactquery, reacthooks Hey Folks, Today it's time to learn how you can abort an ongoing request with ReactQuery. Before moving to the example, I want to introduce the [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). AbortSignal is a javascript interface used to abort ongoing methods. Typically, you can create an AbortSignal by an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController), which simplifies the creation and execution of the AbortSignal. When you create an AbortController (`controller = new AbortController()`) you get an instance of it that has two core concepts: the signal (`controller.signal`) and the abort method (`controller.abort()`). The first one is important because it contains the signal with its status, and the second one is important because it allows you to change the status of the abort from `false` to `true`. Using these two interfaces, in javascript, you can handle out-of-the-box the abort of an HTTP request created with the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) but also with [axios](https://axios-http.com) if you want. When you create a request, both accept a signal and if you abort that signal you can abort the request. AbortSignal has also a static method called `timeout` that accepts a number that describes the timeout time in milliseconds. Using this method you can create easily a signal that changes its status after some time. Very common if you want to handle a timeout of your request. Just a quick example to make you understand the power of these interfaces. ```ts try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const res = await fetch(url, { signal: controller.signal }); const body = await res.json(); } catch (e) { if (e.name === "AbortError") { // Notify the user of abort. // Note this will never be a timeout error! } else { // A network error, or some other problem. console.log(`Type: ${e.name}, Message: ${e.message}`); } } finally { clearTimeout(timeoutId); } ``` In this example, you can see an abort request created with an AbortController. This example uses a setTimeout method to handle the timeout and as you can notice, when the code creates the fetch API passes the signal status to the fetch. In this way, the fetch request and the signal are linked together. This example could be simplified in this way using the AbortSignal timeout method. ```ts try { const res = await fetch(url, { signal: AbortSignal.timeout(5000) }); const body = await res.json(); } catch (e) { if (e.name === "AbortError") { // Notify the user of abort. // Note this will never be a timeout error! } else { // A network error, or some other problem. console.log(`Type: ${e.name}, Message: ${e.message}`); } } ``` Ok, now you have an idea of how AbortSignal and AbortController work, so we can move on to the next step; how can you abort a request with ReactQuer? Good news for you, ReactQuery out-of-the-box handles a signal for you for every query and mutation, and your business it's only decided if you want to use it or not. Let's start with an example ```ts const fetchTodos = async (signal: AbortSignal | undefined): Promise => { const response = await fetch('api/tasks', { signal, }); if (!response.ok) { throw new ResponseError('Failed to fetch todos', response); } return await response.json(); }; interface UseTodos { todos: Todo[]; isLoading: boolean; isFetching: boolean; error?: string; setUserFilter: Dispatch>; } export const useTodos = (): UseTodos => { const client = useQueryClient(); const { data: todos = [], isLoading, isFetching, error, } = useQuery([QUERY_KEY.todos], ({ signal }) => fetchTodos(signal), { refetchOnWindowFocus: false, retry: 2 }); return { todos, isLoading, isFetching, error: mapError(error), setUserFilter, }; }; ``` As you can notice, `useQuery` has a second parameter that contains different options, one of them is the signal. This signal is an AbortSignal that you can use to abort your request if you want. In the example, for instance, the signal is passed to the `fetchTodos` method and then passed to fetch request. As you can understand, aborting a request is a piece of cake 🍰 Ok, I think now you have all the notions to abort an HTTP request in javascript and in ReactQuery too. If you want to go dive into the Abort Request with React query don't miss my Youtube channel 🚀 > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=BL_E7SrymWI](https://www.youtube.com/watch?v=BL_E7SrymWI) I think thats all from this article; I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/10-abort-request) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Part 11 of 12: React Query - Paginated List - **URL**: https://delpuppo.net/blog/react-query/react-query-paginated-list/ - **Published**: Jun 1, 2023 - **Tags**: react, reactquery, reacthooks Hey Folks, Today it's time to learn how you can paginate a list with ReactQuery. It's common to create a pagination of a list to improve the user interface of your platform if you are building a list in your application, but also to limitate the work in your API. Using ReactQuery, building a paginate list is a piece of cake. You have to follow 3 steps: 1. Create a state to save the value of the current page 2. Create a state to save the limit of the pages (the number of elements on every page) 3. Fetch the data every time the user changes one of the previous states. But let's see an example that is easier to understand ```ts const fetchTodos = async ( page: number, limit: number, signal: AbortSignal | undefined ): Promise<{ totals: number; todos: Todo[]; }> => { const response = await fetch(`api/tasks?_page=${page}&_limit=${limit}`, { signal, }); if (!response.ok) { throw new ResponseError('Failed to fetch todos', response); } const todos: Todo[] = await response.json(); const totals = Number.parseInt( response.headers.get('x-total-count') || '0', 10 ); return { totals, todos, }; }; interface UseTodos { todos: Todo[]; isLoading: boolean; isFetching: boolean; error?: string; pages: number; page: number; setPage: Dispatch>; } export const useTodos = (): UseTodos => { const [page, setPage] = useState(1); const [limit] = useState(5); const { data: { todos, totals } = { todos: [], totals: 0, }, isLoading, isFetching, error, } = useQuery( [QUERY_KEY.todos, page, limit], ({ signal }) => fetchTodos(page, limit, signal), { refetchOnWindowFocus: false, retry: 2, } ); return { todos, isLoading, isFetching, error: mapError(error), pages: Math.ceil(totals / limit), page, setPage, }; }; ``` As you can notice, the process to build a pagination with ReactQuery is very simple. The fetch request has to contain the page and the limit (you can handle a pagination also with limit and offset if you want) and in your `useQuery` you have to handle some simple stuff. First, the key of your query must include the page and limit too, second you must pass page and limit to your fetch request. As you can see, building a paginated list with ReactQuery is really a piece of cake, but if want to dive into it don't miss my youtube video about it > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=Ma7-hdNMdWs](https://www.youtube.com/watch?v=Ma7-hdNMdWs) I think thats all from this article; I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/11-paginate-result) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Part 12 of 12: React Query - Infinite Queries - **URL**: https://delpuppo.net/blog/react-query/react-query-infinite-queries/ - **Published**: Jun 7, 2023 - **Tags**: react, reactquery, reacthooks Hey Folks, Today it's time to learn how you can build an infinite query with React Query. To build an infinite query you have to use the `useInfiniteQuery` hook. This hook is similar to the `useQuery` but with some more properties to handle the infinite loading in the best way. The key concepts, if you are working with `useInfiniteQuery` are: - **Key** : in this case, the key is unique for each page, `useInfiniteQuery` handles all the pages inside its state - **page** : each chunk of your infinite query is represented as a page and the first page returned by the `useInfiniteQuery` is undefined. This helps you to understand if it's the first iteration or not. - **hasNextPage** : is a variable returned by the hook and its scope is to determine if another chunk exists or not - **fetchNextPage** : is a function used to fetch the next chunk - **isLoading** : is a flag that determines if there is a loading ongoing (The query has no data yet) - **isFetching** : is a flag that determines if there is a fetch request ongoing - **getNextPageParam** : this function is an option to pass at the hook and it is used to determine if there is another chunk or not after the response ok, let's see the code to be clearer ```ts import { useInfiniteQuery } from '@tanstack/react-query'; import { useState } from 'react'; const fetchTodos = async ( page: number, limit: number, signal: AbortSignal | undefined ): Promise<{ totals: number; todos: Todo[]; }> => { const response = await fetch(`api/tasks?_page=${page + 1}&_limit=${limit}`, { signal, }); if (!response.ok) { throw new ResponseError('Failed to fetch todos', response); } const todos: Todo[] = await response.json(); const totals = Number.parseInt( response.headers.get('x-total-count') || '0', 10 ); return { totals, todos, }; }; interface UseTodos { todos: Todo[]; isLoading: boolean; isFetching: boolean; error?: string; hasNext: boolean; next?: () => void; } export const useTodos = (): UseTodos => { const [limit] = useState(5); const { data, hasNextPage, fetchNextPage, isLoading, isFetching, error } = useInfiniteQuery( [QUERY_KEY.todos], ({ signal, pageParam: page = 0 }) => fetchTodos(page, limit, signal), { refetchOnWindowFocus: false, retry: 2, getNextPageParam: (lastPage, pages) => { if (Math.ceil(lastPage.totals / limit) > pages.length) return pages.length; return undefined; }, } ); return { todos: data?.pages.flatMap(({ todos }) => todos) || [], isLoading, isFetching, error: mapError(error), next: fetchNextPage, hasNext: hasNextPage || false, }; }; ``` As you can notice, the flow is very simple, the hook returns the current page or chunk, you have to handle the fetch request and then you can determine the result and if there is another chunk or not. Another important thing to keep in mind is that the result of the `useInfiniteQuery` is an array and each element of the array contains the data of each chunk. Ok, I suppose you have an idea of how Infinite Queries work in React Query, but if you want to deep into it don't miss my youtube about it. > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=F1KD4GY1iws](https://www.youtube.com/watch?v=F1KD4GY1iws) I think thats all from this article. This is the last post of this series, I hope you enjoyed the content and now you are aware of using ReactQuery in your React application. See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/12-infinitive-scroll) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) ---