Estoy creando una aplicación de películas en React y estoy usando la API de TMDB para eso. Soy completamente nuevo en componentes funcionales y tengo algunas consultas. Primero busco todos los géneros usando una API, y luego itero los géneros uno por uno y llamo a otra API para buscar 20 películas en ese género.
Los datos finales deberían verse así:
[{ genre: 'action', movies: [], }, { genre: 'adventure', movies: [], }]Para que pueda iterar la matriz de objetos anterior y mostrar el género y sus películas correspondientes.
En este momento, estoy atascado con el siguiente código.
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); } }Ahora, yendo a las consultas,
¡Las soluciones son muy apreciadas!
En mi opinión, es mejor mantener sus datos por separado. Me refiero a variedad de géneros y variedad de películas. Entonces tendrás:
const [genres, setGenres] = useState([]); const [movies, setMovies] = useState([]);luego realiza su llamada asíncrona para buscar géneros y configurarla para que indique:
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(); },[]);para averiguar que se configuraron los géneros, puede crear un efecto, que se actualizará cada vez que cambien los géneros:
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]);y finalmente podemos usar React.useMemo para calcular la matriz de resultados que necesitamos según los géneros y las películas, se recalculará cada vez que cambie uno de los parámetros internos:
const resultArray = React.useMemo(() => { return genres.map(genre => ({ genre, movies: movies.filter((movie) => movie.genre_ids.includes(genre.id)) })); }, [genres, movies])Creo que obtendrás una matriz como esta.
[{ genre: 'action', movies: [], }, { genre: 'adventure', movies: [], }]Como ya dijiste, primero debes buscar la variedad de géneros. Luego debe iterar sobre esta matriz y crear una lista de promesas para obtener las películas correspondientes. Una vez que se hayan resuelto estas promesas (usando la función Promise.all ), puede actualizar la variable de estado 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; } } debe adaptar MOVIE_URL a sus necesidades, realmente no sé cuál es la URL real, imagino que necesita pasar el valor del género como un parámetro de ruta o un parámetro de consulta.
ahora puede llamar a esta función en el useEffect para cargar las películas cuando se procesa el componente.
function App() { const [movieData, setMovieData] = useState([]); useEffect(() => { fetchMoviesGroupByGenre() .then(data => setMovieData(data)) .catch(...); }, []); return ... }En términos de rendimiento, no mostrará nada antes de que se hayan obtenido todas las películas.
Si es un problema para usted, puede actualizar la variable de estado tan pronto como la API le dé una respuesta.
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; } }Por lo que entendí lo que quieres hacer es
setMovieData(prevMovieData => ([ ...prevMovieData, { genre: genreFromApiCall, movies: moviesFromApiCall } ]) )