Obsessed with React and teaching. I help people become Frontend Developers. Living with my fiancee and Yorkshire Terrier 🐶 in Poland.
Let's learn how to fetch data in React using fetch, axios, and react-query. We'll create a custom useApi hook and build a simple Pokemon application using pokeAPI.
In this article, we'll build a Pokemon application using PokeAPI and Material UI GridList component. To do that, I'll guide you through how to fetch data in React with hooks.
Function components in React are great but data fetching with them can be cumbersome at first. When I moved to using hooks it took me some time to grasp what's going on. If you're starting out, check out Hooks introduction from the official documentation. Additionally, read about useState and useEffect hooks as we'll need them.
I'll start from using fetch to get the data from PokeAPI, then refactor the code for using a custom
useApihook with axios, and end up with react-query as it's an amazing library.
In the end, you should have a fair understanding of hooks, data fetching in React and build the below Pokemon list application. 😎
Important: React devs are working on Suspense API and server-side components, both of these exciting features can impact the ideology behind data fetching - still, hooks are instrumental to understand all of these concepts and I highly encourage you to dive into this article.
Fetch PokeAPI data with hooks
Let's start from the mentioned
fetch, we'll use it with async/await syntax. It's pretty much a syntatic sugar built on top of promises.
Note: I'm using a codesandbox to develop this application but you could set up a local repository with e.g. CRA or any favorite boilerplate you want.
Fetching data is not so straightforward
First, we're setting the initial
pokemonsstate variable to
(empty array) using the
useStatehook. Then, using
async/await(please, notice the usage of
.json()to get an actual JSON from HTTP response), we get the data from pokeAPI, limiting it to the first 100 Pokemons. After that, we save
setPokemonsfunction. In the end, we return a simple
JSXto render the list of Pokemon names.
However, we end up in an infinity loop.
useEffecthook runs when the component mounts and when it updates. We're setting the data of Pokemons after we receive it from the
fetchwhich updates the state and triggers
useEffectto run again.
To prevent that, we need to add an empty list of dependencies to
useEffecthook, then it'll run only once - when the component mounts. And that's what we need.
No more infinity loop. 🎉 But still, when I run the above code in the sandbox environment, we get this warning: "Effect callbacks are synchronous to prevent race conditions. Put the async function inside".
async/awaitreturns a promise but
useEffectshould return either nothing or a cleanup function. Let's fix that.
Thanks to our
fetchDatafunction inside of the
useEffecthook, we got rid of the warning. 🎉
If you'd like to get the data when a user types something in the search field (e.g. to find a particular Pokemon), you can pass additional dependencies to the
dependency list on
useEffect. This will be a bit problematic as each time the user types in,
useEffectwill fetch the data. You'll need to use some debounce technique to solve it.
If you need to get the data on button click, create an async. function and assign it to the
The above solutions are out of the scope of this guide. I covered them in a separate article as there are not too many "out of the box" resources to explain these topics.
What about error handling and loading state?
In a real-world project, you can't let the application crash when something goes wrong. There should be a gently alert to the user which is often called a fallback UI. We won't use React ErrorBoundary for this project for simplicity reasons but instead, we'll handle the error inside of the component directly using the Alert component.
For loading, we'll show a spinner component when data is still being fetched using the CircularProgress and Grid components.
We've added state responsible for error and loading, introduced try...catch to make sure we catch all the possible states. When the error happens, we'll show the
Alertcomponent with an error message.
Loading is a bit more tricky as we set a default value to
true. It was intended as we don't want to render an empty page when there is no data yet. Instead,
CircularProgresswill show up.
Everything is almost perfect but we're mixing logic with rendering in one component. You're guessing right, it's time to create our custom
useApihook component and refactor the code a little bit.
useApi hook with axios
Let's have a bit of fresh air and switch to axios for data fetching. It's a nice library similar to
fetchbut has less boilerplate, better error handling, and can monitor data loading (totally not needed for our case 😅).
I've written a Knowledge Pill for fetching data from an API using
fetchvia a custom hook. The above code snippet is almost identical with the exception that it uses
axios.get(less boilerplate, no check for
.ok) instead and is named
useFetchlike in the pill. For purposes of our Pokemon application, we don't need to use any other methods than the
To learn more about custom
useApihook I highly recommend going through the above pill and comparing both solutions (please, play a little bit in the codesandbox with pokeAPI too).
It looks clearer when all the logic is extracted in our
useApihook. As you can see on the highlighted code,
data. We need to extract
data, rename it for declarativity purposes to
pokemonsand render as before.
What if there already exists a hook for data fetching? 🙈
Unfortunately (for me), I'm not a precursor of custom hooks for data fetching. We mentioned
react-queryin the introduction. Let's see how that can simplify our life. (I'll still leave
useApihook in the code sandbox for reference).
I skipped the setup of the
react-queryand focused on
useQuery. You can read documentation to configure it in your project or take a look in the codesandbox (index.js).
useQueryhook has a very similar structure to our custom hook with small exceptions. The first parameter is the query name, in our case
"pokemons"string argument. The second parameter is a function to execute the query, it could be
axiosbut we used
getPokemonsfunction to showcase
useQueryhook works with it.
It appears, we didn't really need to write any of the data fetching code and just use
react-query. Although, I hope this data fetching journey opened your eyes, and either it's an abstraction or not, you know what you're doing when writing asynchronous code in React. 🙇♂️
As we spent quite some time on the data, let's make it pretty and shiny with the
Using GridList and fetching Pokemon image
Going from the top, I added a couple of
GridListrelated components for styling purposes. Then, I've implemented
getPokemonfunction to receive the Pokemon data based on provided
url. The function was used inside of a newly created
PokemonTilecomponent with a combination of
react-query(please, notice anonymous function usage to pass an additional
After getting Pokemon data, I was able to render the
PokemonTileto present it on the screen. Outer
Appcomponent renders a list of
PokemonTilecomponents based on the
pokemonsquery (limited to 100).
REST API, GraphQL, gRPC?
All our API calls were based on REST API so far but GraphQL is a great alternative. There is also a gRPC buzzword but I didn't work with it at all.
GraphQL is great as it has a single endpoint to rule them all, the client (you, browser) can decide how much data should be fetched, everything happens through queries and mutations. Additionally, it has an auto-generated schema feature (helps a lot to understand API structure, similar to e.g. Swagger, just built-in). On top of that, there is e.g. apollo-client library for React which supports cache management (you know, cache invalidation and naming things... 🙈) and has amazing hooks support out of the box.
Not going into the details. However, I've written another article to present simple usage of GraphQL query through pokeAPI with browser fetch. Interested? Check it out!
The Frontend journey never ends. To become a top-quality React Developer you need to get a good hang of data fetching. This was my first big series about explaining crucial topics based on project building, hopefully, you had some fun and finished the project with me along the way. 😊
... but if not, let's take a look at the preview of it one more time to avoid scrolling all the way top.
As I already mentioned in the related pill, time for you to turn this project into the best Pokedex online ever created! 🔥
Big thanks for reading the article, you're awesome! 🙇♂️
You can also find me on:
Thanks for all the support. ❤️
THE GUIDE TO BEAT FRONTEND INTERVIEW
Let's build a community that will help each other.