如何使用 useState 设置对象数组的状态?
How to set state of an Array of objects using useState?
我正在用 React 构建一个电影应用程序,为此我正在使用 TMDB api。我对功能组件完全陌生,有一些疑问。首先,我使用 api 获取所有类型,然后逐个迭代类型并调用另一个 API 来获取该类型的 20 部电影。
最终数据应该是这样的:
[{
genre: 'action',
movies: [],
},
{
genre: 'adventure',
movies: [],
}]
这样我就可以迭代上面的对象数组并显示类型及其对应的电影。
现在,我被下面的代码卡住了。
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);
}
}
现在开始提问,
- 现在,movieData 是一个数组,只包含流派。如何也添加电影并将其更改为对象数组?
- 如何迭代流派并在movieData中设置对应的电影?
- 还有其他 easy/efficient 方法吗?
非常感谢解决方案!
如您所说,您首先需要获取流派数组。然后你必须遍历这个数组并创建一个承诺列表来获取相应的电影。一旦这些承诺得到解决(使用 Promise.all
函数),您可以更新状态变量 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;
}
}
你必须使 MOVIE_URL
适应你的需要,我真的不知道真正的 url 是什么,我想你需要将流派值作为路径参数或查询传递参数
您现在可以在 useEffect
挂钩中调用此函数以在渲染组件时加载影片。
function App() {
const [movieData, setMovieData] = useState([]);
useEffect(() => {
fetchMoviesGroupByGenre()
.then(data => setMovieData(data))
.catch(...);
}, []);
return ...
}
在性能方面,在所有电影都被提取之前,您不会显示任何内容。
如果这对您来说是个问题,您可以在 api 给您答复后立即更新状态变量
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;
}
}
据我了解你想做的是
setMovieData(prevMovieData => ([
...prevMovieData,
{ genre: genreFromApiCall, movies: moviesFromApiCall }
])
)
在我看来,最好将您的数据分开保存。我的意思是多种类型和多种电影。所以你将拥有:
const [genres, setGenres] = useState([]);
const [movies, setMovies] = useState([]);
然后您进行异步调用以获取流派并将其设置为状态:
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();
},[]);
要确定流派已设置,您可以创建一个效果,每次流派更改时都会更新:
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]);
最后我们可以使用React.useMemo根据流派和电影计算出我们需要的结果数组,每次内部参数之一发生变化时都会重新计算:
const resultArray = React.useMemo(() => {
return genres.map(genre => ({
genre,
movies: movies.filter((movie) => movie.genre_ids.includes(genre.id))
}));
}, [genres, movies])
相信你会得到这样的数组
[{
genre: 'action',
movies: [],
},
{
genre: 'adventure',
movies: [],
}]
我正在用 React 构建一个电影应用程序,为此我正在使用 TMDB api。我对功能组件完全陌生,有一些疑问。首先,我使用 api 获取所有类型,然后逐个迭代类型并调用另一个 API 来获取该类型的 20 部电影。
最终数据应该是这样的:
[{
genre: 'action',
movies: [],
},
{
genre: 'adventure',
movies: [],
}]
这样我就可以迭代上面的对象数组并显示类型及其对应的电影。
现在,我被下面的代码卡住了。
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);
}
}
现在开始提问,
- 现在,movieData 是一个数组,只包含流派。如何也添加电影并将其更改为对象数组?
- 如何迭代流派并在movieData中设置对应的电影?
- 还有其他 easy/efficient 方法吗?
非常感谢解决方案!
如您所说,您首先需要获取流派数组。然后你必须遍历这个数组并创建一个承诺列表来获取相应的电影。一旦这些承诺得到解决(使用 Promise.all
函数),您可以更新状态变量 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;
}
}
你必须使 MOVIE_URL
适应你的需要,我真的不知道真正的 url 是什么,我想你需要将流派值作为路径参数或查询传递参数
您现在可以在 useEffect
挂钩中调用此函数以在渲染组件时加载影片。
function App() {
const [movieData, setMovieData] = useState([]);
useEffect(() => {
fetchMoviesGroupByGenre()
.then(data => setMovieData(data))
.catch(...);
}, []);
return ...
}
在性能方面,在所有电影都被提取之前,您不会显示任何内容。
如果这对您来说是个问题,您可以在 api 给您答复后立即更新状态变量
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;
}
}
据我了解你想做的是
setMovieData(prevMovieData => ([
...prevMovieData,
{ genre: genreFromApiCall, movies: moviesFromApiCall }
])
)
在我看来,最好将您的数据分开保存。我的意思是多种类型和多种电影。所以你将拥有:
const [genres, setGenres] = useState([]);
const [movies, setMovies] = useState([]);
然后您进行异步调用以获取流派并将其设置为状态:
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();
},[]);
要确定流派已设置,您可以创建一个效果,每次流派更改时都会更新:
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]);
最后我们可以使用React.useMemo根据流派和电影计算出我们需要的结果数组,每次内部参数之一发生变化时都会重新计算:
const resultArray = React.useMemo(() => {
return genres.map(genre => ({
genre,
movies: movies.filter((movie) => movie.genre_ids.includes(genre.id))
}));
}, [genres, movies])
相信你会得到这样的数组
[{
genre: 'action',
movies: [],
},
{
genre: 'adventure',
movies: [],
}]