According to their official Docs,
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.
It basically maintains the cache of server data on the client. All you need to do is to let react query know when it needs to refresh the data in the cache.
With React Query you get,
And a few other interesting stuff. Let’s look into how we can get started with this amazing library.
We assume that you already know the basics of React and React Hooks .
Create a new react project using the command
npx create-react-app my-app
This is my setup
Let’s install react query using the command below,
npm install react-query
or
yarn add react-query
Create a new file with the name Todos.js and the following code and mount it in App.js
import React from "react"; export default function Todos() { const data = [{ userId: 1, title: "Buy groceries", id: 1, completed: false }]; return ( <> <ul style={{ listStyle: "none" }}> {data?.map((todo) => ( <li key={todo.id} style={{ color: todo.completed ? "green" : "orange" }} className="todo-title" > {todo.title} - {todo.completed ? "Completed" : "Pending"} </li> ))} </ul> <div className="pages"> <button disabled>Previous page</button> <span>Page 0</span> <button disabled>Next page</button> </div> <hr /> </> ); }
This is how it should look once mounted. (I’ve also added some styles in CSS file)
Next, the two most important things are,
Let’s go ahead and import QueryClient and QueryClientProvider from react-query and implement it in the App.js file as we need to do this in the root component.
We create an instance of QueryClient using
const queryClient = new QueryClient();
and then we pass this instance to QueryClientProvider via props, which is wrapping everything that we are returning in App.js so all children can access client config and cache.
import "./styles.css"; import Todos from "./Todos"; import { QueryClient, QueryClientProvider } from "react-query"; const queryClient = new QueryClient(); export default function App() { return ( <QueryClientProvider client={queryClient}> <div className="App"> <h1>Todo List</h1> <Todos /> </div> </QueryClientProvider> ); }
This is how your App.js should look like.
Now, Its time to talk about Queries and useQuery Hook.
According to the docs,
A query is a declarative dependency on an asynchronous source of data that is tied to a unique key.
and useQuery hook takes a unique key for the query and a function that returns a promise to fetch data from a server. The query results returned by useQuery contains all of the information about the query that your component needs.
Alright, enough talking. Let’s head to Todos.js component and create an async function above our Todos component that fetches a list of todos from JSON placeholder API in the same file.
const fetchAllTodos = async () => { const res = await fetch("https://jsonplaceholder.typicode.com/todos"); return res.json(); };
Now, to implement useQuery hook, we need to import it as follow,
import { useQuery } from "react-query";
and then replace,
const data = [{ userId: 1, title: "Buy groceries", id: 1, completed: false }];
with
const { data, isError, error, isLoading } = useQuery( "todos", fetchAllTodos );
Let’s understand what’s happening in the above code snippet.
useQuery takes 3 arguments, first one is the query key and the second one is the function that returns a promise and the last one is a config object which provides additional info to useQuery hook on how to handle the query. We will dive into the third argument in another article as its optional so we are good with the first two arguments for now. We are further destructuring data, isError, error, isLoading from useQuery hook. It has a lot more things which you can destructure and use which we will be discussing later.
With all these things combined we can structure our Todos component in the following way,
import React from "react"; import { useQuery } from "react-query"; const fetchAllTodos = async () => { const res = await fetch("https://jsonplaceholder.typicode.com/todos"); return res.json(); }; export default function Todos() { const { data, isError, error, isLoading } = useQuery("todos", fetchAllTodos); if (isLoading) { return <h3>Loading</h3>; } if (isError) { return <h3>Oops, Something went wrong {error.toString()}</h3>; } return ( <> <ul style={{ listStyle: "none" }}> {data?.map((todo) => ( <li key={todo.id} style={{ color: todo.completed ? "green" : "orange" }} className="todo-title" > {todo.title} - {todo.completed ? "Completed" : "Pending"} </li> ))} </ul> <div className="pages"> <button disabled>Previous page</button> <span>Page 0</span> <button disabled>Next page</button> </div> <hr /> </> ); }
Here’s the final code of what we built in this article.
That’s it! You did it. I know sometimes it’s difficult to wrap your head around these concepts but react-query is a powerful tool that can help you maintain the server’s state on the frontend easily with caching.
We will be discussing more on other topics like pagination with react-query, query invalidations, mutations, prefetching, devtools in depth later in other articles. So stay tuned.
Quick Links
Legal Stuff