Home > OS >  Why I my React Project goes into infinite loop and keep rendering pokemons on the window?
Why I my React Project goes into infinite loop and keep rendering pokemons on the window?

Time:04-04

When I run npm start react goes into infinite loop and keeps showing me the same Pokemons again and again. I want to see each pokemon only one time on the screen.

I also got the following error in the console:

Encountered two children with the same key, 6. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version

import React, {useState} from 'react';
import Board from './sub-main/Board';

function Main(Props) {

    const [pokemonList, setPokemonList] = useState(['mewtwo', 'mew', 'arceus', 'rayquaza', 'lugia', 'alakazam', 'ditto', 'gengar', 'charizard', 'dragonite', 'blastoise', 'gyarados'])

  return (
    <div>
        <Board pokemonList={pokemonList}/>
    </div>
  );
}

export default Main;

This is Board.js

import React, {useState, useEffect} from 'react';
import '../styles/board.css'
function Board(props) {
    
    const [pokemons, setPokemons] = useState([])
    
    useEffect(() => {
        
        const getPokemon = async (pokemon, index) => {
            const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${pokemon}/`);
            const data = await response.json();
            const pokemonData = { id: index, name: pokemon, url: data.sprites.other.dream_world.front_default}
            console.log(pokemonData)
            setPokemons(pokemons => pokemons.concat(pokemonData))
        }
      
        props.pokemonList.forEach((pokemon, index) => {
            getPokemon(pokemon,index)
            
        })
    }, [])

    
  return (
    <div id='board'>
        {pokemons.map((pokemon) => {
            return <div key={pokemon.id} className="pokemonCard">
                        <p>{pokemon.name}</p>
                        <img src={pokemon.url} alt={`${pokemon.name} pic`}/>
                   </div>
        })}
    </div>
  );
}

export default Board;

CodePudding user response:

You are doing setState on each iteration of props.pokemonList, React won't batch the state updates in async operations which leads to inifinite re-render.

Try to set the state after resolving all the values using Promise.all

useEffect(() => {
  const getPokemon = async (pokemon, index) => {
    const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${pokemon}/`);
    const data = await response.json();
    const pokemonData = { id: index, name: pokemon, url: data.sprites.other.dream_world.front_default }
    return pokemonData
  }

  const pokemonPromisesArr = props.pokemonList.map((pokemon, index) => getPokemon(pokemon, index))
  Promise.all(pokemonPromisesArr).then(pokemonArr => setPokemons(pokemonArr))
}, [])

CodePudding user response:

I'm taking a punt here but it sounds like you're running in React's Strict Mode; is your app getting bootstrapped something like this?:

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

and the warnings you're seeing are due to its deliberate double-invoking of functional components to flush out possible issues.

The "same key" warning is an excellent tell-tale that you're doing something not-quite-right however, and in this case I think it's the combination of a forEach and array.concat. As another guess, I'm thinking you might have done it this way to get your server API calls to fire concurrently?

You want exactly as many pokemonData objects as you provide Pokemon names, so map is the right tool for the job, and we'll use Promise.all to wait for all the API calls to resolve before atomically updating state, rather than concating things:

// Pull this helper function outside of your component
const getPokemon = async (pokemon, index) => {
  const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${pokemon}/`);
  const data = await response.json();
  const pokemonData = {
    id: index,
    name: pokemon,
    url: data.sprites.other.dream_world.front_default
  };
  console.log(pokemonData);
  return pokemonData;
};

function Board(props) {
  const [pokemons, setPokemons] = useState([]);

  useEffect(() => {
    const loadPokemons = async () => {
      const pokemonDataPromises = props.pokemonList.map((pokemon, index) => 
     {
        return getPokemon(pokemon, index);
      });

      const pokemonData = await Promise.all(pokemonDataPromises);
      setPokemons(pokemonData);
    };
    loadPokemons();
  }, [props.pokemonList]);

   ...

}
  • Related