What is react-query? Why should we use react-query?

Introduction

You might have heard or seen others mention the react-query library quite often. Why should we use react-query when we can use useEffect to fetch data from an API? This article will answer that question.

What is react-query?

react-query is a set of hooks for fetching, caching, and updating asynchronous data in React.

Simply put, react-query is a library for fetching, caching, and updating asynchronous data when used in React.


Why should we use react-query?

Normally, there are many ways to fetch data from a server. Some popular methods include using useEffect and useState, or using a state management library like redux.

export const Page = () => {
const [data, setData] = useState();
useEffect(() => {
const loadData = async () => {
const response = await fetch(hostAPI).then(response => response.json());
setData(response)
}
loadData()
})
return <DisplayData />
}

This method works fine, but it’s quite cumbersome. You have to write a bunch of code just to fetch data from the server and display it to the user. This method has the advantage of being simple to use, easy to read, and easy to understand. However, in cases where complex processing is required, such as caching response data, you need to implement a lot more. Luckily, react-query will support us in these cases.


Using react-query

React-query provides simple hooks to fetch and update data from the server. It supports caching, re-fetching, and much more.

First, let’s learn how to fetch data using useQuery.


useQuery

To use the useQuery hook, you must pass at least two parameters:

  • The first parameter is the query key, which is a string[] data type, used for re-fetching, caching, and sharing data between components.
  • The second parameter is a function typically used to call an API, which returns a promise used to resolve data or throw an error.
  • The third parameter is the options (optional) that useQuery supports.

useQuery returns necessary information such as data, isFetching, or isError. We then use this information to display appropriate information.

Here’s an example:

import {useQuery} from '@tanstack/react-query'

const Page = () => {
const { data, isFetching, isError } = await useQuery(['todo'], () => fetch(hostAPI).then(response => response.json()))
if (isFetching) {
return <Loading />
}
if (isError) {
return <Error />
}
return <DisplayData />
}

useMutation

useMutation is used to create/update/delete data. This hook includes the following parameters:

  • The first parameter is the mutation key (similar to the query key).
  • The second parameter is a function that returns a promise to resolve data or throw an error.
  • The third parameter is the options.

The result returned by the useMutation hook is isFetching, isError, data similar to useQuery, in which there is an additional mutate function used to pass params when calling the API to create/update data.

Here's how to use it:

import {useMutation} from '@tanstack/react-query'

export const addProduct = (product: Partial<Product>): Promise<Product> =>
fetch(`${hostAPI}/add`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(product),
})
.then(res => res.json())
.catch(() => {
throw new Error('Failed addProduct')
})

function Page() {
const {mutate, isError, isFetching, data} = useMutation(['add-product'], addProduct)
return (
<div>
<button onClick={() => mutate({id: 1, title: 'title 1'})}>
Add product
</button>
</div>
)
}

Example of use with option param

Pre-fetching data

In case you have a list of articles belonging to a series on a certain topic, you would want the system to automatically fetch data of the next article when the user clicks on any article. We can use react-query to implement this feature as follows:

import {useQuery, useQueryClient} from '@tanstack/react-query'

export default function Index() {
const [id, setId] = useState<number>(1)
const queryClient = useQueryClient()
const {data, error, isFetching} = useQuery(['list-post', id], () => getPostDetail(id), {
onSuccess: () => {
const nextID = id + 1
// prefetch data, next time must request this queryKey it will use the cache data
queryClient.prefetchQuery(['list-post', nextID], () => getPostDetail(nextID))
},
onError: console.error, // log error
})
return <DisplayData />
}

Here, the option object takes in two functions: onSuccess and onError:

  • onSuccess runs when the query is executed successfully. I use the method queryClient.prefetchQuery (useQueryClient) to prefetch the data of the next post. If the user accesses any post, the data of the next post will be fetched in advance so that it can be used immediately when the user accesses that post.
  • onError runs if the query encounters an error.


Optimistic UI updates

This is the case when we want to update the UI before receiving the response data result (and will update the accurate data later).

For example, when clicking like, the UI will render as liked during the API call process. After getting the response data, the UI will be updated according to the corresponding status. This way, the user won't have to wait too long for this function (helping to enhance the user experience).

export default function Page() {
const [isLike, setLike] = useState<boolean>(false)
const {mutate} = useMutation(['change-status'], updateLike, {
onMutate: () => setLike(true), // set status while waiting reponse
onSuccess: setLike, // set status after receiving response data
onError: console.error,
})
return isLike
}

  • useMutate runs before a mutation is successful or encounters an error. At this point, we’ll make preliminary changes to the state here.
  • onSuccess runs after the mutation has been executed (regardless of success or error). At this point, we’ll update the state depending on the response data.


Conclusion

React-query is a library that readily supports many features related to data requests, caching, and more. It also has many other great features that you can check out in more detail here.

Don't hesitate to leave your thoughts in the comments section, and remember to like, share, and follow for more insightful content in the future!

Comments

Popular posts from this blog

Kubernetes Practice Series

NodeJS Practice Series

Docker Practice Series

React Practice Series

Sitemap

Setting up Kubernetes Dashboard with Kind

Deploying a NodeJS Server on Google Kubernetes Engine

DevOps Practice Series

A Handy Guide to Using Dynamic Import in JavaScript

Create API Gateway with fast-gateway