如何使用 useState 设置对象数组的状态?
Posted
技术标签:
【中文标题】如何使用 useState 设置对象数组的状态?【英文标题】:How to set state of an Array of objects using useState? 【发布时间】:2021-10-28 16:12:46 【问题描述】:我正在用 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中迭代流派并设置对应的电影?
还有其他简单/有效的方法吗?
非常感谢您的解决方案!
【问题讨论】:
如果您可以添加 api 的示例 json,将会有所帮助。 你的最终输出:为什么需要一个对象数组?单个对象会更有意义。 action: [], adventure: []
.
@HassanImam Codesandbox 链接包含两个 api:codesandbox.io/s/infallible-greider-vf18t?file=/src/App.js
@Andy 你能详细说明怎么做吗?也许是一个简短的解决方案?
【参考方案1】:
在我看来,最好单独保存您的数据。我的意思是一系列类型和一系列电影。所以你将拥有:
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: [],
]
【讨论】:
我遵循了相同的步骤,但在 resultArray 部分出现语法错误。 Codesandbox 链接供您参考:[链接]codesandbox.io/s/infallible-greider-vf18t?file=/src/App.js 当然很抱歉我已经修复了这部分代码,我忘了括号 这是您的工作示例codesandbox.io/s/sleepy-perlman-it6c3【参考方案2】:正如您已经说过的,您首先需要获取流派数组。然后你必须遍历这个数组并创建一个 Promise 列表来获取相应的电影。一旦解决了这些承诺(使用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;
【讨论】:
【参考方案3】:据我了解,你想做的是
setMovieData(prevMovieData => ([
...prevMovieData,
genre: genreFromApiCall, movies: moviesFromApiCall
])
)
【讨论】:
请添加更多详细信息以扩展您的答案,例如工作代码或文档引用。以上是关于如何使用 useState 设置对象数组的状态?的主要内容,如果未能解决你的问题,请参考以下文章
useState 钩子一次只能设置一个对象,并将同一个数组的另一个对象返回到初始状态