Skip to content
Advertisement

Change component state – REACT

I’m currently working on a project with the Pokemon API and i’m facing a problem. I want to change the value parameter in the async function getPokemonTypes(), but the value I receive in handleSelect() is not working. On the console.log(value), the value changes every time I select a different option. Could someone tell me what I’m doing wrong?

import React from 'react'
import { useState, useEffect } from "react";
import { Link } from 'react-router-dom'

async function getPokemonTypes(value) {

    const response = await fetch(`https://pokeapi.co/api/v2/type/${value}`)
    const data = await response.json()
    console.log(data)
    return data
}

async function getPokemonInfo(pokemonId) {

    const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${pokemonId}`)
    const data = await response.json()
    return data
}


export const PokemonTypesCard = () => {

    const [pokemonTypesCard, setPokemonTypesCard] = useState({})
    const [pokemonIdNumber, setPokemonIdNumber] = useState([])
    const [value, setValue] = useState('normal')

    const handleSelect = (value) => {
        setValue(value)
      }
      console.log(value)
    useEffect(() => {

        async function fetchData() {

            const pokemonTypesCard = await getPokemonTypes(value)
            const pokemonIdText = pokemonTypesCard.pokemon.map((item) => {
                return item.pokemon.name
            })
            const data = pokemonIdText.map(async (pokemonId) => {
                return (
                    await getPokemonInfo(pokemonId)
                )
            })
            const pokemonIdNumber = await Promise.all(data)
            setPokemonIdNumber(pokemonIdNumber)
            setPokemonTypesCard(pokemonTypesCard)
        }
        fetchData()
    }, [])

    return (
        <section>

            <div>
                <label htmlFor='pokemon-types'>Choose a pokemon type</label>
                <form>
                    <select onChange={(event) => handleSelect(event.target.value)}
        value={value}>
                        <option value='normal'>Normal</option>
                        <option value='fighting'>Fighting</option>
                        <option value='flying'>Flying</option>
                        <option value='poison'>Poison</option>
                        <option value='ground'>Ground</option>
                        <option value='rock'>Rock</option>
                        <option value='bug'>Bug</option>
                        <option value='ghost'>Ghost</option>
                        <option value='steel'>Steel</option>
                        <option value='fire'>Fire</option>
                        <option value='water'>Water</option>
                        <option value='grass'>Grass</option>
                        <option value='electric'>Electric</option>
                        <option value='psychic'>Psychic</option>
                        <option value='ice'>Ice</option>
                        <option value='dragon'>Dragon</option>
                        <option value='dark'>Dark</option>
                        <option value='fairy'>Fairy</option>
                        <option value='shadow'>Shadow</option>
                    </select>
                </form>

            </div>

            {<div>
                <ul>
                    {!pokemonIdNumber ? '' : pokemonIdNumber.map((item, index) =>
                        <li key={index}>
                            <Link to={`/pokemon/${item.id}`}>
                                <img
                                    src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${item.id}.png`}
                                    alt={item.name}
                                />
                            </Link>
                            <p>{item.name}</p>
                        </li>
                    )}
                </ul>
            </div>}

        </section>
    );
}

Advertisement

Answer

You need to add type to the dependnecies array of useEffect:

useEffect(() => {
    async function fetchData() {
        const pokemonTypesCard = await getPokemonTypes(value);
        const pokemonIdText = pokemonTypesCard.pokemon.map((item); => {
            return item.pokemon.name;
        });
        const data = pokemonIdText.map(async (pokemonId) => {
            return (
                await getPokemonInfo(pokemonId)
            );
        });
        const pokemonIdNumber = await Promise.all(data);
        setPokemonIdNumber(pokemonIdNumber);
        setPokemonTypesCard(pokemonTypesCard);
    }

    fetchData();
}, [value]); // <= HERE

Keep in mind this code has some issues, as you might end up seeing data for a type that doesn’t match the one in the URL if something like this happens:

  • You select fire and getPokemonTypes('fire') is called.
  • You select to ice and getPokemonTypes('ice') is called.
  • getPokemonTypes('ice') finishes loading and the rest of the fetchData function executes.
  • getPokemonTypes('fire') finishes loading and the rest of the fetchData function executes.
  • The selected option is now ice but see data from fire.

The proper way to do it would be like this:

useEffect(() => {
    let shouldUpdate = true;

    async function fetchData() {
        const pokemonTypesCard = await getPokemonTypes(value);

        if (!shouldUpdate) return;

        const pokemonIdText = pokemonTypesCard.pokemon.map((item) => {
            return item.pokemon.name;
        });

        const data = pokemonIdText.map((pokemonId) => {
            return getPokemonInfo(pokemonId);
        });

        const pokemonIdNumber = await Promise.all(data);

        if (!shouldUpdate) return;

        setPokemonIdNumber(pokemonIdNumber);
        setPokemonTypesCard(pokemonTypesCard);
    }

    fetchData();

    // This cleanup function will be called if the `useEffect`'s
    // dependencies change, so if you run it twice in a row for
    // different types, when the result of the first one arrives,
    // it will be discarded:
    return () => {
        shouldUpdate = false;
    };
}, [value]);

Also, you have an error here:

const data = pokemonIdText.map(async (pokemonId) => {
    return (
        await getPokemonInfo(pokemonId)
    );
});

const pokemonIdNumber = await Promise.all(data);

You want to store all those promises in the data array and await them all together with Promise.all below, but you are instead awaiting them one by one. It should be like this:

const data = pokemonIdText.map((pokemonId) => {
    return getPokemonInfo(pokemonId);
});

const pokemonIdNumber = await Promise.all(data);
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement