How to set state of an Array of objects using useState?

I’m building a movies app in React and I’m using TMDB’s api for that. I’m completely new to functional components and have a few queries. First I fetch all the genres using an api, and then iterate the genres one by one and call another API for fetching 20 movies in that genre.

The final data should look like this:

[{
    genre: 'action',
    movies: [],
},
{
    genre: 'adventure',
    movies: [],
}]

So that I can iterate the above array of objects and display the genre and its corresponding movies.

Right now, I’m stuck with the below code.

const [movieData, setMovieData] = useState([]);

const fetchGenres = async () => {

    const url = `https://api.themoviedb.org/3/genre/movie/list?api_key=xxx&language=en-US`;
    
    try {
      const res = await fetch(url);
      const data = await res.json();
      setMovieData(data.genres);      
    } catch (err) {
      console.error(err);
    }
}

Now, coming to the queries,

  1. Right now, movieData is an array and consists of only genres. How to a add movies too and change it to an array of objects ?
  2. How to iterate the genres and set corresponding movies in movieData?
  3. Any other easy/efficient way of doing this?

Solutions are much appreciated!

Answer

To my mind it’s better to keep your data separately. I mean array of genres and array of movies. So you will have:

const [genres, setGenres] = useState([]);
const [movies, setMovies] = useState([]);

then you make your async call to fetch genres and set it to state:

const fetchGenres = async () => {
    const url = `https://baseurl/3/genre/movie/list?api_key=token&language=en-US`;

    try {
        const res = await fetch(url);
        const data = await res.json();
        setGenres(data.genres);
    } catch (err) {
        console.error(err);
    }
};


React.useEffect(()=>{
    fetchGenres();
},[]);

to find out that the genres were set you can create an effect, that will update each time when genres change:

async function fetchMovies() {
    let result = [];

    for (const genre of genres) {
        const url = `https://baseurl/3/discover/movie?api_key=token&with_genres=${genre.id}`;
        const res = await fetch(url);
        const { results } = await res.json();

        result = [...result, ...results];
    }

    setMovies(result);
}

React.useEffect(() => {
    fetchMovies();
}, [genres]);

and finally we can use React.useMemo to calculate the result array we need according to genres and movies, it will be recalculated each time when one of the inner params change:

const resultArray = React.useMemo(() => {
    return genres.map(genre => ({
        genre,
        movies: movies.filter((movie) => movie.genre_ids.includes(genre.id))
    }));
}, [genres, movies])

I believe you will get an array like this

[{
    genre: 'action',
    movies: [],
},
{
    genre: 'adventure',
    movies: [],
}]