javascript - How to set state of an Array of objects using useState? - Stack Overflow

I'm building a movies app in React and I'm using TMDB's api for that. I'm pletely n

I'm building a movies app in React and I'm using TMDB's api for that. I'm pletely new to functional ponents 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 = `;language=en-US`;
    
    try {
      const res = await fetch(url);
      const data = await res.json();
      setMovieData(data.genres);      
    } catch (err) {
      console.error(err);
    }
}

Now, ing 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!

I'm building a movies app in React and I'm using TMDB's api for that. I'm pletely new to functional ponents 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/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, ing 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!

Share Improve this question asked Aug 29, 2021 at 16:58 ramukramuk 471 silver badge6 bronze badges 4
  • It would help if you could add sample json of your api. – Hassan Imam Commented Aug 29, 2021 at 17:02
  • Your final output: why does that need to be an array of objects? An single object would make more sense. { action: [], adventure: [] }. – Andy Commented Aug 29, 2021 at 17:05
  • @HassanImam Codesandbox link containing both the api's : codesandbox.io/s/infallible-greider-vf18t?file=/src/App.js – ramuk Commented Aug 30, 2021 at 3:52
  • @Andy Can you please elaborate how to do that? A brief solution maybe? – ramuk Commented Aug 30, 2021 at 3:53
Add a ment  | 

3 Answers 3

Reset to default 4

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: [],
}]

As you already said, you first need to fetch the array of genres. Then you must iterate over this array and create a list of promises to fetch the corresponding movies. Once theses promises have been resolved (using Promise.all function) you can update the state variable movieData.

const GENRE_URL = `https://.../genre/movie/list?api_key=xxx&language=en-US`;

const MOVIE_URL = `https://.../movie/list?api_key=xxx&language=en-US&genre=`;

async function fetchGenres() {
  try {
    const genres = await fetch(GENRE_URL).then(res => res.json());

    const moviePromises = genres.map(genre => {
      return fetch(MOVIE_URL + genre)
        .then(res => res.json())
        // build an object containing 2 keys `genre` and `movies`
        .then(movies => ({genre, movies})); 
    });

    return await Promise.all(moviePromises);  
  } catch (err) {
    console.error(err);
    throw err;
  }
}

you must adapt the MOVIE_URL to your needs, I don"t really know what the real url is, I imagine you need to pass the genre value as a path param or a query param.

you can now call this function in the useEffect hook to load the movies when the ponent is rendered.

function App() {
  const [movieData, setMovieData] = useState([]);

  useEffect(() => {
    fetchMoviesGroupByGenre()
      .then(data => setMovieData(data))
      .catch(...);
  }, []);

  return ...
}

In term of performance, you will not display anything before all the movies have been fetched.

If it's a problem for you, you can update the state variable as soon as the api gives you an answer

async function fetchGenres() {
  try {
    const genres = await fetch(GENRE_URL).then(res => res.json());
    
    const moviePromises = genres.map(genre => {
      return fetch(MOVIE_URL + genre)
        .then(res => res.json())
        .then(movies => {
          const item = {genre, movies};
          setMovieData(prevMovieData => [...prevMovieData, item]);
          return item;
        ]);
    });

    return await Promise.all(moviePromises);  
  } catch (err) {
    console.error(err);
    throw err;
  }
}

From what I understood what you want to do is

setMovieData(prevMovieData => ([
...prevMovieData,
{ genre: genreFromApiCall, movies: moviesFromApiCall }
])
)

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744929656a4601653.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信