0

I'm trying to fill an array using the useState Hook. This project is made with Next.js and Tailwind.

In each iteration of the map function I'd like to add an item to the end of the array until there are no items left.

My goal is to have a stateful array that is filled with items so that I can manipulate all the items.

Thanks.

index.js

import { useState } from "react";
import Head from "next/head";
import Image from "next/image";
import requests from "../components/requests";
import Content from "../components/Content";
import Header from "../components/Header";

export const getStaticProps = async () => {
  const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=2000/");
  const pokemonData = await res.json();

  return {
    props: { pokemon: pokemonData },
  };
};

export default function Home({ pokemon }) {
  return (
    <>
      <Header />
      <main className="flex flex-col items-center justify-center sm:flex-row sm:flex-wrap">
        {pokemon.results.map((pokemon) => (
          <Content key={pokemon.name} pokemon={pokemon} />
        ))}
      </main>
    </>
  );
}

Content.js

import React, { useState, useEffect } from "react";
import Image from "next/image";

const Content = ({ pokemon }) => {
  const [Pokemon, setPokemon] = useState({ name: [] });
  // name.forEach((name) => name[0].toUpperCase() + name.substring(1));

  const url = pokemon.url;
  const pokemonIndex = url.split("/")[url.split("/").length - 2];
  const imageUrl = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemonIndex}.png`;

  useEffect(() => {
    setPokemon((prevState) => ({
      ...prevState,
      name: prevState.name.concat(pokemon.name),
    }));
  }, [pokemon]);

  console.log(Pokemon);

  return (
    <div className="p-5">
      <h1 className="text-center text-3xl font-bold">{pokemon.name}</h1>
      {/* <Image src={Pokemon.imageUrl} width="250" height="250" /> */}
    </div>
  );
};

export default Content;
Alexander
  • 29
  • 1
  • 6
  • Your question was initially unclear, and it appeared as if you were just missing `[]` in `concat` call. But now it appears you are trying to create a state having all rendered Pokemons, which should be shared by all of your `Content` components. Firstly, do you actually need to modify `Pokemons` from each `Content` if not then you can simply pass it as a prop. Second, are you sure you can't handle then from the parent using some additional props passed to `Content` or likewise. Third, if both of them is not the case then refer [this answer](https://stackoverflow.com/a/53455474). – brc-dd Jul 26 '21 at 07:28

2 Answers2

1

You could create a new array and spread the contents of the old array with the new element added at the end.

useEffect(() => {
    setPokemon((prevState) => ({
      ...prevState,
      name: [
        ...preveState.name,
        pokemon.name
      ]
    }));
  }, [pokemon]);
Hyetigran
  • 1,150
  • 3
  • 11
  • 29
  • I've tried this but each iteration gives me a new array with one item (the current Pokemon name only). I also get a lot of empty arrays before it start creating the new arrays. – Alexander Jul 25 '21 at 23:17
0

I assume, you want whole list of pokemons (pokemon.results) inside your Content component. One easier way to have it this way is to pass a separate prop pokemons={pokemons.results} to Content component.

  • This somewhat works. However, on each map() iteration the state is updated again to the same pokemons.results array. Is that okay? What do you think about mapping items inside the component instead of mapping the component itself? – Alexander Jul 25 '21 at 23:35
  • So even if you want to update list on every render, the state will be updated. As for each map, a new separate component is being created. So I don't think there would be any harm in passing the whole Pokemon list for each map component. Won't affect performance at all, imo. Also I agree with your point as well, you can map all pokemons inside the component. That's perfectly fine and correct way. – krupa Panchal Jul 29 '21 at 03:25