A first look at React Query

A first look at React Query

Β·

4 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 exactly 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'll see it will show a loading indicator, but on a second return, it's neatly cached and show the Pokemon right away.

A first look at React Query

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 also 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

We'll have to wrap our app with the QueryClientProvider to use react query.

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 then 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 that 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 simply wrapping of code will make sure 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 else 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 we do 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

Did you find this article valuable?

Support Daily Dev Tips by becoming a sponsor. Any amount is appreciated!