Handling data with 

Domitrius Clark

Advocate Engineer @ Cloudinary




 what is react-query and why use it?

react-query plainly, is a set of hooks that assist you in dealing with asynchronous data in whatever way you'd prefer


As long as your request returns a promise, react-query can make use of it.



useQuery | usePaginatedQuery | useInfiniteQuery

You're probably wondering what happens when feeding a request through react-query.


Let's begin by taking a look at an example of the useQuery hook.

const { data, status } = useQuery(
  query.id && ['user', query.id], 
  async (key, id) => {
    const user = await fetch(`/api/user/${id}`);
	return user.json();


async fetch function

useQuery's return vars


We'll start by talking about the queryKey argument. The queryKey is used to define the cache key that will hold onto your data. For example ['user', query.id] sets up our cache to attach to user.

Next is your async fetch function . You see here we're using a REST endpoint with our fetch function to get our response. You can also use things like axios or graphql-request . As was stated, anything that returns a Promise can be fed to the useQuery hooks.

  async (key, id) => {
    const user = await fetch(`/api/user/${id}`);
	return user.json();

{data, status, error} — the variables we destructure from useQuery allow us ways to set up our UI for incoming data, loading status, and error's your request return.

Possible Returns

- data

- latestData

- error

- isFetching

- resolvedData

- refetch

- failureCount

The usePaginatedQuery and useInfiniteQuery hooks allow us to deal with fetching our data in chunks that return variables we're used to ie status & error , but also allow us to hook into other variables like resolvedData and isFetching which again help us prepare our UI for data updates and serving cached data from previous requests.

  const {
  } = usePaginatedQuery(
    ['topAnimeList', page], 
    async (key, page) => {
      const topAnime = await fetch(`https://api.jikan.moe/v3/top/anime/${page}`);

      return topAnime.json();

Let's check out a quick example of how our user query can be done with graphql.

const GET_USER = `
  query GetUser($id: ID!) {
    getUser(id: $id) {

const { data, status } = useQuery("user", id], async (key, id) => {
  const user = await request(`https://yourendpoint/graphql`,GET_USER, { id })};

  return user;


We've seen how to deal with requesting our data. Now how do we handle manipulating our data with a POST, PUT, or DELETE.



const [createPost] = useMutation(
  async ({ post }) => {
    await fetch('/api/create/post', {
      method: "POST",
      body: JSON.stringify(post)
  onSuccess: () => {

Here we are given our useMutation

 function that fires off your useMutation hook.

useMutation's secondary object argument comes with some built in utilities:

  • onMutate
  • onSuccess
  • onError
  • onSettled
  • throwOnError
  • useErrorBoundary

Last, but most certainly not least, is the power of the queryCache.

function removeCharacterFromFavorites({ character }) {
  queryCache.setQueryData('favoriteCharacters', prevData => {
    return prevData.filter(data => data.id !== character.id)

function fetchFavoriteCharacters(key) {
  return queryCache.getQueryData('favoriteCharacters');

The queryCache comes built in with a slew of different built in helpers, that allow you to lightly and forcefully make updates, fact check, and refresh your cache