dannisis
dannisis

Reputation: 462

set State when fetch response in useEffect is a array

This is fetching from Pokemon API which response with an array that I want to pass useState variable initialized as empty array with setValue function.

React component is below:

import React, { useState, useEffect } from "react";

export default function Pokemones() {
  const [pokemons, setPokemons] = useState([]);

  useEffect(() => {
    async function fetchData() {
      // Call fetch
      const res = await fetch("https://pokeapi.co/api/v2/pokemon/");
      // Pull out the data
      const json = await res.json();
      // Save the data to array
      console.log(json.results);
 }

    fetchData();
  }, []);

  return (
    <ul>
      {pokemons.map((pokemon) => (
        <li>
          <p>Name = {pokemon.name}</p>
          <p>URL = {pokemon.name}</p>
        </li>
      ))}
    </ul>
  );
}

I tried directly set setPokemons(json.results); or by json.results.map, received same error. I did other tries but I got similar errors

      // atfet console.log and before fetchData function 

      let pok = [];
      json.results.map((p) => (pok = [...pok, p]));
      console.log(pok);
      setPokemons(pok);
    }

The error received on consola is:

Uncaught Error: Objects are not valid as a React child (found: object with keys 
{name, url}). If you meant to render a collection of children, use an array instead.

I guess that array is not passing to variable pokemons but I am able to see in console the array. what part is that I am missing. thanks

Upvotes: 0

Views: 130

Answers (2)

Mulan
Mulan

Reputation: 135227

minimum example

Here's a minimum working example you can run here and verify the results in your own browser.

function App() {
  const [pokemon, setPokemon] = React.useState([])
  
  React.useEffect(_ => {
    fetch("https://pokeapi.co/api/v2/pokemon/")
      .then(res => res.json())
      .then(res => setPokemon(res.results))
      .catch(console.error)
  }, [])
  
  return <ul>
    {pokemon.map(p =>
      <li key={p.name}>
        <a href={p.url}>{p.name}</a>
      </li>
    )}
  </ul>
}

ReactDOM.render(<App/>, document.querySelector("#app"))
ul { list-style-type: none; padding-left: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>

going further

We can expand the app's functionality a bit by making the list item's clickable and show the selected Pokémon details -

📸 pokedex app screenshot
pokedex preview

To do this we break the program down into three parts. First, the List component -

function List({ pokemon, onClick }) {
  return <ul id="list">
    {pokemon.map(p =>
      <li key={p.name} onClick={onClick(p)}>
        {p.name}
      </li>
    )}
  </ul>
}

Second, the Details component -

function Details({ url }) {
  const [data, setData] = React.useState(null)
  
  React.useEffect(_ => {
    url && fetch(url)
      .then(res => res.json())
      .then(res => setData(res))
      .catch(console.error)
  }, [url])
  
  return data
    ? <pre id="details">
        <h3>{data.name}</h3>
        {JSON.stringify(data, null, 2)}
      </pre>
    : <pre id="details"><h3>Select a Pokémon in the list</h3></pre>
}

And finally the Pokedex component which brings it all together -

function Pokedex() {
  const [pokemon, setPokemon] = React.useState([])
  const [selected, setSelected] = React.useState(null)
  
  const selectPokemon = pokemon => event =>
    setSelected(pokemon.url)
  
  React.useEffect(_ => {
    fetch("https://pokeapi.co/api/v2/pokemon/")
      .then(res => res.json())
      .then(res => setPokemon(res.results))
      .catch(console.error)
  }, [])
  
  return <div id="pokedex">
    <List pokemon={pokemon} onClick={selectPokemon} />
    <Details url={selected} />
  </div>
}

Hopefully this gives you a better idea of how React apps are put together. You can run the Pokedex demo below -

function Pokedex() {
  const [pokemon, setPokemon] = React.useState([])
  const [selected, setSelected] = React.useState(null)
  
  const selectPokemon = pokemon => event =>
    setSelected(pokemon.url)
  
  React.useEffect(_ => {
    fetch("https://pokeapi.co/api/v2/pokemon/")
      .then(res => res.json())
      .then(res => setPokemon(res.results))
      .catch(console.error)
  }, [])
  
  return <div id="pokedex">
    <List pokemon={pokemon} onClick={selectPokemon} />
    <Details url={selected} />
  </div>
}

function List({ pokemon, onClick }) {
  return <ul id="list">
    {pokemon.map(p =>
      <li key={p.name} onClick={onClick(p)}>
        {p.name}
      </li>
    )}
  </ul>
}

function Details({ url }) {
  const [data, setData] = React.useState(null)
  
  React.useEffect(_ => {
    url && fetch(url)
      .then(res => res.json())
      .then(res => setData(res))
      .catch(console.error)
  }, [url])
  
  return data
    ? <pre id="details">
        <h3>{data.name}</h3>
        {JSON.stringify(data, null, 2)}
      </pre>
    : <pre id="details"><h3>Select a Pokémon in the list</h3></pre>
}

ReactDOM.render(<Pokedex/>, document.querySelector("#app"))
body, ul, pre { margin: 0; padding: 0; }
ul { list-style-type: none; }
li { cursor: pointer; }
#pokedex { display: flex; }
#list { margin-right: 2rem; }
#details { flex-grow: 1; background-color: #ffc; max-height: 100vh; overflow-y: scroll; padding-left: 1rem; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>

Upvotes: 1

Lokesh Kumar Meena
Lokesh Kumar Meena

Reputation: 501

I hope this helps.

import React, { useState, useEffect } from "react";
    
    export default function Pokemones() {
      const [pokemons, setPokemons] = useState([]);
    
      useEffect(() => {
        async function fetchData() {
          // Call fetch
          const res = await fetch("https://pokeapi.co/api/v2/pokemon/");
          // Pull out the data
          const json = await res.json();
          // Save the data to array
          console.log(json.results);
          setPokemons(json.results);
     }
    
        fetchData();
      }, []);
    
      return (
        <ul>
          {pokemons.map((pokemon, index) => {
           return <li key={index}>
              <p>Name = {pokemon.name}</p>
              <p>URL = {pokemon.name}</p>
            </li>
          })}
        </ul>
      );
    }

Upvotes: 2

Related Questions