Fetching data with ReactJs using Pokemon API

Last updated Jul 24, 2024 Published Feb 11, 2021

The content here is under the Attribution 4.0 International (CC BY 4.0) license

Since the introduction of functional components and hooks in reactjs, the way that developers handle data fetching has changed. Before that, developers were used to handle code for a given special function that triggers at specific times during the life cycle of the component, for example: beforeMount, componentWillMount, componentMounted and others. For each of those methods, the reactjs API was defined and it was the door to hook into the component’s lifecycle. However, since the introduction of react hooks, those explicit triggers of the component lifecycle have changed and been replaced by hooks. One of them is the useEffect that is used to sync external changes into reactjs components. In this post, we will go over the useEffect hook with common examples that developers face when using reactjs and consuming an API. For each example, we will use the create-react-app for scaffolding the project.

What is a side effect?

Larsen depicts this approach as syncing the data flow between the external state with the internal ReactJs state [1].

useEffect - on mount

The analogy of useEffect with onMoun if not encouraged, however, this thinking helps to shift the mental model to the pure functional way that has been adopted by ReactJs.

npx create-react-app fetch-data

Next up, open up the project in your favourite IDE I am going to use web storm:

webstorm . 

Let’s start with fetching data from the Pokemon API, in the App component let’s use state and effect to do that when the component mounts:

// App.jsx
import './App.css';  
import {useEffect, useState} from "react";  
  
function App() {  
  const [pokemons, setPokemons] = useState([]);
  
  useEffect(() => {  
    fetch('https://pokeapi.co/api/v2/pokemon?limit=10&offset=0')  
        .then(response => response.json())  
        .then(data => setPokemons(data.results));  
  }, []);
  
  return (  
    <div className="App">  
      {pokemons.map(pokemon => <p key={pokemon.name}>{pokemon.name}</p>)}  
    </div>  
  );  
}  
  
export default App;

For this simple scenario, the function App has no dependencies that it needs to be able to trigger the fetch again. This start to pop out when we start to move fetch into a dependency. Let’s pretend we want to extract the fetch to another file so it can be reused, as follows:

// fetch.js
export const fetchData = async (url) => {  
  const response = await fetch(url);
  const json = await response.json();
  return json.results;  
};

// App.jsx
import './App.css';  
import {useEffect, useState} from "react";  
import { fetchData } from './fetch';
  
function App() {  
  const [pokemons, setPokemons] = useState([]);

  useEffect(() => {  
    fetchData('https://pokeapi.co/api/v2/pokemon?limit=10&offset=0')  
        .then(data => setPokemons(data));  
  }, []);
  
  return (
    <div className="App">
      {pokemons.map(pokemon => <p key={pokemon.name}>{pokemon.name}</p>)}
    </div>
  );
}

export default App;

What are the dependencies anyway?

React uses dependencies to sync the external state with the internal state of a component. That way, react knows when something has changed outside of its scope and reacts to it. The official documentation documentation categorizes three types of dependencies of a component:

  • Empty array
  • Dependencies with array
  • No dependencies

How to avoid unnecessary rerenders?

  • Avoid unnecessary re-renders in ReactJS
  • How to fetch data with React Hooks
    • this tutorial has exactly an example of a nasty loop
  • In the course Reactjs Masterclass from Shubham Sarda he depicts a scenario in which a changing URL should trigger an API call to an endpoint when he does that, the app enters an infinite loop, because useEffect cannot determine if a function has changed or not (even though the linter says to add the fetch function inside the dependency array), the following picture depicts the issue:
    • the fix is simply adding the useCallback hook
    • In another video, the author also uses useRef to prevent rerendering, this time, useRef is used because the body is an object and this object is passed as a reference to a custom hook:

Resources

Final solution

References

  1. [1]J. Larsen, React Hooks in Action: With Suspense and Concurrent Mode. Simon and Schuster, 2021.