'Why does one of this work but not the other - fetch and useState - react/JS

I am looking for an explanation as to why the below does not work

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

export default function Genrenavbar() {
  const [mygenres, setMygenres] = useState([]);

  useEffect(() => {
    async function APIrequests() {
      const response = await fetch(
        `https://api.themoviedb.org/3/genre/movie/list?api_key=${process.env.REACT_APP_MY_TEST_API}&language=en-US`
      );
      const movie = await response.json();
      console.log(movie);
      setMygenres(movie);
      console.log(mygenres);
    }
    APIrequests();
  }, []);
 
   const help = mygenres.genres.map((elem) => console.log(elem.name));

  return (
    <div>
      <h1>Hello</h1>
    </div>
  );
}

But this one works

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

export default function Genrenavbar() {
   const [mygenres, setMygenres] = useState({ genres: [] });

  useEffect(() => {
    async function APIrequests() {
      const response = await fetch(
        `https://api.themoviedb.org/3/genre/movie/list?api_key=${process.env.REACT_APP_MY_TEST_API}&language=en-US`
      );
      const movie = await response.json();
      console.log(movie);
      setMygenres(movie);
      console.log(mygenres);
    }
    APIrequests();
  }, []);
 
   const help = mygenres.genres.map((elem) => console.log(elem.name));

  return (
    <div>
      <h1>Hello</h1>
    </div>
  );
}

The only difference is the first line being

const [mygenres, setMygenres] = useState([]);

change into

const [mygenres, setMygenres] = useState({ genres: [] });

On a side note, it also wont work if done like the below

const [mygenres, setMygenres] = useState([{}]);

or

const [mygenres, setMygenres] = useState({});

The below is the return my API fetch requests gives

genres: Array(19)
0: {id: 28, name: 'Action'}
1: {id: 12, name: 'Adventure'}
2: {id: 16, name: 'Animation'}
3: {id: 35, name: 'Comedy'}
4: {id: 80, name: 'Crime'}
5: {id: 99, name: 'Documentary'}
6: {id: 18, name: 'Drama'}
7: {id: 10751, name: 'Family'}
8: {id: 14, name: 'Fantasy'}
9: {id: 36, name: 'History'}
10: {id: 27, name: 'Horror'}
11: {id: 10402, name: 'Music'}
12: {id: 9648, name: 'Mystery'}
13: {id: 10749, name: 'Romance'}
14: {id: 878, name: 'Science Fiction'}
15: {id: 10770, name: 'TV Movie'}
16: {id: 53, name: 'Thriller'}
17: {id: 10752, name: 'War'}
18: {id: 37, name: 'Western'}

I don't understand why we need to put genres whitch is a key from my response in the state. Does it have something to do with putting a fetch request in a state? When I create a local array of objects outside of the fetch request, I can easily just put the variable holding the array in the state using setState without the need to put the key (from my array of objects) into the state and my mapping function to map the state will work just fine.

However, if I do not put a key in the example above, my mapping function will not work on the state (this is despite the fact that I can console.log out the state).

If you are going to say that when I initially map I am mapping an empty array then I don't understand why this would be? Shouldn't the response already be returned (as I am using await for the fetch to be returned) and shouldn't it already be set as the state before the time I begin mapping (as I setState inside my useEffect and starting mapping outside my useEffect)?.

If someone can quick a description of the order of events that occur with the reasoning why one of these works (and why we NEED to put the key) but the other ones do not work that would be great.

Thanks.



Solution 1:[1]

You're defining the state as just an array:

const [mygenres, setMygenres] = useState([]);

And then trying to read a property called genres on that array:

const help = mygenres.genres.map((elem) => console.log(elem.name));

Arrays don't have a property called genres, so it's undefined. And trying to call .map() on undefined will produce an error.

Alternatively, when you define an object with a property called genres:

const [mygenres, setMygenres] = useState({ genres: [] });

Then you can read the property genres from that object. And that property is an array, so you can call .map() on it:

const help = mygenres.genres.map((elem) => console.log(elem.name));

Overall the key difference is that if you want an object with a custom named property which is an array, just definining an array by itself doesn't satisfy that. You need to define the object, with that property.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 David