skip to Main Content

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


  1. You could use an async function inside the hook, await for the fetch and finally set the data:

    useEffect(() => {
        (async () => {
            for (const mon of pokemonList) {
                const result = await fetchPokemon(mon);
                pokemonArray.push(filterPokemonData(result));
            };
            setPokemon(pokemonArray);
        })();
    }, []);
    

    The reason why you did not receive the output you wished is due to the fetch requests not being synchronised with setPokemon

    Login or Signup to reply.
  2. 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:

    import { useEffect, useState } from "react";
    import { fetchPokemon } from "api/fetchPokemon";
    
    export const useFetchPokemon = (pokemonList: string[]) => {
        const [pokemon, setPokemon] = useState<Array<Pokemon>>([]);
    
        useEffect(() => {
            const fetchData = async () => {
                try {
                    const promises = pokemonList.map((mon) => fetchPokemon(mon));
                    const pokemonData = await Promise.all(promises);
                    const filteredPokemon = pokemonData.map(filterPokemonData);
                    setPokemon(filteredPokemon);
                } catch (error) {
                    console.error("Error fetching Pokémon:", error);
                }
            };
    
            fetchData();
        }, [pokemonList]);
    
        return pokemon;
    };
    

    In this updated code:

    1. We use Promise.all to fetch Pokémon data for all the items in the pokemonList array concurrently, which should be faster than making sequential requests.

    2. We handle any errors that might occur during the data fetching process and log them to the console.

    3. The custom hook now takes the pokemonList array as a dependency for the useEffect, ensuring that it will re-run whenever pokemonList changes.

    4. We simplify the code by directly setting the filteredPokemon array in the setPokemon function. This avoids the need for an intermediate pokemonArray variable.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search