I have a list of Pokemon names that I am looping through and using to call the PokeApi. Then, I want to take the returned data and output a simple display.
I am encountering issues saving the data and returning it, but I don’t know where I’m going wrong.
My output never has all of the pokemon that I would expect it to have and the return values are inconsistent with previous ones. So there might be issues with data being overwritten or synchronicity, but none of my attempts to fix it have worked.
Here’s the code:
App.tsx
import { gen1List } from 'data';
import React from 'react';
import { Pokemon } from 'models';
import { useFetchPokemon } from 'api/useFetchPokemon';
export const App = () => {
const gen1Pokemon = useFetchPokemon(gen1List);
return (
<>
{gen1Pokemon.map((pokemon: Pokemon, index: number) => (
<div key={index}>
{pokemon.name}
<img src={pokemon.sprite} alt={pokemon.name} />
</div>
))}
</>
);
};
useFetchPokemon.ts
import { Pokemon, filterPokemonData } from "models";
import { useEffect, useState } from "react"
import { fetchPokemon } from "api/fetchPokemon";
export const useFetchPokemon = (pokemonList: string[]) => {
const [pokemon, setPokemon] = useState<Pokemon[]>([]);
const pokemonArray: Pokemon[] = [];
useEffect(() => {
for (const mon of pokemonList) {
fetchPokemon(mon).then((result) => {
pokemonArray.push(filterPokemonData(result));
});
};
setPokemon(pokemonArray);
}, []);
return pokemon;
};
fetchPokemon.ts
export const fetchPokemon = async (name: string): Promise<any> => {
const response = await fetch('https://pokeapi.co/api/v2/pokemon/' + name);
return await response.json();
};
models.ts
export interface Pokemon {
name: string;
sprite: string;
};
export const filterPokemonData = (pokemon): Pokemon => {
return {
name: pokemon.name,
sprite: pokemon.sprites.front_default,
}
};
data.ts
export const gen1List: string[] = [
'bulbasaur',
'venusaur',
'charizard',
];
I’ve tried making the call a lot of different ways, with or without a hook. I’ve experimented with using useCallback, putting the loop in its own async function and calling that, setting the state inside the loop, etc.
2
Answers
You could use an async function inside the hook, await for the fetch and finally set the data:
The reason why you did not receive the output you wished is due to the fetch requests not being synchronised with
setPokemon
To make the code more efficient and easy to understand, you can use
Promise.all
to fetch data for all the Pokémon simultaneously and then update the state when all the promises have resolved. Here’s an updated version of your custom hook:In this updated code:
We use
Promise.all
to fetch Pokémon data for all the items in thepokemonList
array concurrently, which should be faster than making sequential requests.We handle any errors that might occur during the data fetching process and log them to the console.
The custom hook now takes the
pokemonList
array as a dependency for theuseEffect
, ensuring that it will re-run wheneverpokemonList
changes.We simplify the code by directly setting the
filteredPokemon
array in thesetPokemon
function. This avoids the need for an intermediatepokemonArray
variable.