Subscribe

A first look at React Query

✍️

Caching and data fetching made easy with React Query

5 Feb, 2022 · 6 min read

State management can be a bit difficult in React, the way you have to deal with data, make sure it’s cached, re-fetching it when needed, and the list goes on.

Luckily for us, this is precisely where react-query comes in. React query can handle all those and many more things for us.

For this first example, we’ll build a list with Pokemon names. When we click on one of the names, it loads that specific Pokemon’s details. The first time you see it, it will show a loading indicator, but on a second return, it’s neatly cached and shows the Pokemon immediately.

Setting up the React Query project and dependencies

Let’s get started as it’s easier to explain as we go so you can see what will happen.

First, let’s create a new React project:

npx create-react-app react-query

Then we’ll need to install react-query:

npm i react-query

And while we are here, let’s install axios to handle requests for us.

npm i axios

That should give us a great starting point. From here, you can open up the project in your favorite terminal.

Using React query

To use react query, we’ll have to wrap our app with the QueryClientProvider.

To do this, open up the App.js file and modify the app to look like this:

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

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <div>Our app</div>
    </QueryClientProvider>
  );
}

Instead of this div, we want to render a list of Pokemon that the user can click on.

We’ll use a state that will contain the Pokemon that was clicked, so let’s start by modifying our code to look like that.

function App() {
  const [pokemon, setPokemon] = useState(null);
  return (
    <QueryClientProvider client={queryClient}>
      {pokemon ? (
        <Pokemon pokemon={pokemon} setPokemon={setPokemon} />
      ) : (
        <PokemonList setPokemon={setPokemon} />
      )}
    </QueryClientProvider>
  );
}

We defined a state and passed the state to either the Pokemon component or the PokemonList component.

Loading data with React query

Let’s start with the list. First of all, we’ll need a function that will be able to query an API.

We then wrap that query in a useQuery hook so react query can handle all the caching.

function usePokemonList() {
  return useQuery('pokemon', async () => {
    const { data } = await axios.get(
      'https://pokeapi.co/api/v2/pokemon?offset=0&limit=50'
    );
    return data.results;
  });
}

The first element we pass to the useQuery hook is the key for this query. In our case, the key is pokemon.

Then we fetch 50 Pokemon from our API and return the results.

And yes, this simple wrapping of code will ensure react query does all the hard work for us.

Let me show you how we can use this:

function PokemonList({ setPokemon }) {
  const { isLoading, data } = usePokemonList();
  return (
    <div>
      {isLoading ? (
        <p>loading...</p>
      ) : (
        <ul>
          {data.map((pokemon) => (
            <li>
              <a onClick={() => setPokemon(pokemon.name)} href='#'>
                {pokemon.name}
              </a>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

We use the Pokemon list function we just created in the component above. We can extract isLoading and the actual data object from it.

Then we return a loading state while it’s loading, or we render a list of Pokemon.

When the user clicks one of the Pokemon, we invoke the setPokemon function to handle the state.

Retrieving single results

Now that we have our list let’s work on the Pokemon component. This component should fetch a single Pokemon based on our state.

But before doing that, we should create a new function that can request the API for the details.

function usePokemon(name) {
  return useQuery(['pokemon', name], async () => {
    const { data } = await axios.get(
      `https://pokeapi.co/api/v2/pokemon/${name}`
    );
    return data;
  });
}

This is similar to what we saw in the list, but we pass an extra attribute to the key property. This will make this query unique for just this Pokemon request.

Let’s move on to the actual component:

function Pokemon({ pokemon, setPokemon }) {
  const { isLoading, data } = usePokemon(pokemon);
  return (
    <div>
      <a href='#' onClick={() => setPokemon(null)}>
        Back to the list
      </a>
      {isLoading ? (
        <p>loading...</p>
      ) : (
        <div>
          <h1>{pokemon}</h1>
          <img src={data.sprites.front_default} alt={pokemon} />
        </div>
      )}
    </div>
  );
}

Here we use the function we just created and again show loading while it’s still loading and render the Pokemon and an image once it’s done.

And there you go. This is how easy it can be to leverage react query to do all the heavy cache management for us.

I set up a Code Sandbox example you can play around with.

Note: If it’s already cached, try to add ?v2 to the single Pokemon requests.

Thank you for reading, and let’s connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Spread the knowledge with fellow developers on Twitter
Tweet this tip
Powered by Webmentions - Learn more

Read next 📖

Conditional wrapping in React

11 Dec, 2022 · 4 min read

Conditional wrapping in React

Have you tried React classnames?

26 Oct, 2022 · 2 min read

Have you tried React classnames?

Join 2099 devs and subscribe to my newsletter