DEV Community

Ivan
Ivan

Posted on

React Query 101

I've always looked at react-query from far away, before I was mainly working with GraphQL and Apollo was a great solution for my querying needs.

Fast forward a couple years and now I am at a different job and I am not using GraphQL anymore. Having heard a lot about react-query I decided to give it a try and see how good it really is.

And...

Insert drum roll

It's actually pretty great, it has all the goodies I enjoyed in Apollo and it's not restricted to GraphQL.

Here is a quick guide to demonstrate some of the many benefits/features of react-query.

First let's create a react project, create-react-app is perfect for this simple demo app.

npx create-react-app react-query --template typescript

# or

yarn create react-app react-query --template typescript
Enter fullscreen mode Exit fullscreen mode

Yes, I am adding TypeScript to this one page application, I can't deal with those yellow icons anymore

Now navigate inside the react-query folder that was just created.

If you are in the terminal just do

cd react-query
Enter fullscreen mode Exit fullscreen mode

Now let's install react-query

 npm i react-query
 # or
 yarn add react-query
Enter fullscreen mode Exit fullscreen mode

Let's also install axios to use it instead of fetch

 npm i axios
 # or
 yarn add axios
Enter fullscreen mode Exit fullscreen mode

Now inside src/App.tsx past the following code

import React from 'react';
import './App.css';

function App() {
  return (
    <div className="App">
      <h1>Random Food Generator</h1>
      <button>Generate</button>
      <img src="insert food url here" 
        alt="food"/>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

So the promise of this app is pretty simple, we press a button to get a randomly generated dish. In order to do this we will use the food api ,react-query and axios.

First we need to wrap our in app inside a <QueryProvider /> to connect the queryClient.

Inside src/index.tsx let's import QueryClient and create a new client for the app.

// ...other code

import { QueryClient, QueryClientProvider } from "react-query";

// ...other imports

const queryClient = new QueryClient();
Enter fullscreen mode Exit fullscreen mode

And let's use the QueryClientPovider with the new client we just created.

Still inside src/index.tsx

ReactDOM.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>,
  document.getElementById("root")
);
Enter fullscreen mode Exit fullscreen mode

And that's all we need to start firing our queries!

The magic of useQuery

Hooks are here to stay and most of my favorite libraries are using them, react-query is no exception. The useQuery hook is pretty cool, we give it a unique key and a function that returns a promise. In exchange we get the data and other useful props.

Let's see in action, inside src/App.tsx let's add the following code.

First let's import useQuery and axios.

import { useQuery } from "react-query";
import axios from "axios";
Enter fullscreen mode Exit fullscreen mode

Now inside the App component let's use useQuery

  const { data } = useQuery("food", () =>
    axios.get("https://foodish-api.herokuapp.com/api/")
  );
Enter fullscreen mode Exit fullscreen mode

and now in the JSX

 return (
    <div className="App">
      <h1>Random Food Generator</h1>
      <button>Generate</button>
      <img src={data?.image} alt="food" />
    </div>
  );
Enter fullscreen mode Exit fullscreen mode

So far so good, everything seems to work, but if you pay attention you may notice some strange behavior. For example if you go to another tab in your browser, when you come back the query is re-fetched. This is one of the things that caught me off guard when trying react-query for the first time, I remember not having a clue of what was going on and just switching to something else.

Well, apparently it's important to read the docs. react-query has some defaults that can be aggressive but you can easily change them to what you need or you are used to.

These are my defaults.

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 3600,
      refetchOnWindowFocus: false,
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

So now we are not re-fetching on window focus and we actually have a stale time.

Besides data we have access to other props that can help us build a better UI by telling us the state of the query.

Let's take a look.

const { data, isFetching, isError, refetch } = useQuery("food", () =>
    axios.get("https://foodish-api.herokuapp.com/api/")
  );
Enter fullscreen mode Exit fullscreen mode

There are more, but we will use these for now. I think these props are pretty self explanatory, let's use them to let the user know what's going on with the query.

function App() {
  const { data, isFetching, isError, refetch } = useQuery("food", () =>
    axios.get("https://foodish-api.herokuapp.com/api/")
  );

  if (isError) {
    return <p>Oops an error happened</p>;
  }

  return (
    <div className="App">
      <h1>Random Food Generator</h1>
      <button type="button" onClick={() => refetch()}>
        Generate
      </button>
      {isFetching ? (
        <p>Loading...</p>
      ) : (
        <img src={data?.data?.image} alt="food" />
      )}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

So first we check for any errors, then connect the button to the refetch function and finally display a loading state when the image is being fetched.

Can we do all of these with fetch?
Yeah of course, but it would have taken a lot more code. A useEffect for fetching the initial data, creating state for the loading and error, and putting everything into a function to do the re-fetch.

This is only scratching the surface of what react-query can do, we didn't even look into the cache how it can replace state management tools like redux.

Anyways, I hope this got you interested in checking react-query, because there is so much more that you can do.

Over and out.

Code: https://github.com/ivanms1/react-query-101

PS: Miss you GraphQL :(

Top comments (3)

Collapse
 
ledu6 profile image
Benoit LEGER-DERVILLE • Edited

@tannerlinsley @ivanms1 Thanks for this Post, but 2 more questions?

  1. Why this new context

<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>

is now mandatory in v3 ? This is more code to do/maintain!

  1. A which level do you put this wrapper? App level? other? or Colocation is preferred? There is no doc on this point about QueryClientProvider
Collapse
 
tannerlinsley profile image
Tanner Linsley

Try using GraphQL-Request or any other client with React Query ;)

react-query.tanstack.com/graphql

Collapse
 
ivanms1 profile image
Ivan

Will do!
Thanks for all you do Tanner, big fan of your work.