You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中如何用useState设置对象数组的状态?(TMDB电影应用场景)

问题1解答:如何生成符合要求的初始结构

拿到TMDB返回的genres数组后,不要直接赋值给state,先做一次结构转换即可,把原有代码里的setMovieData(data.genres)替换为如下逻辑:

const formattedGenres = data.genres.map(item => ({
  genreId: item.id, // 保留分类id用于后续请求对应电影
  genre: item.name,
  movies: [] // 初始化空电影数组
}))
setMovieData(formattedGenres)

转换后的movieData就是你需要的目标结构。

问题2解答:遍历拉取分类电影并更新状态

推荐两种常用方案,可按需选择:

  • 方案1:等待所有分类电影拉取完成后一次性更新,逻辑更简单
    用Promise.all并发发起所有分类的电影请求,全部返回后一次性更新state,避免频繁触发组件渲染,代码示例:
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();
    // 第一步:生成初始结构
    const formattedGenres = data.genres.map(item => ({
      genreId: item.id,
      genre: item.name,
      movies: []
    }))
    // 第二步:并发请求所有分类的电影
    const movieRequests = formattedGenres.map(async (genreItem) => {
      const movieUrl = `https://api.themoviedb.org/3/discover/movie?api_key=xxx&with_genres=${genreItem.genreId}&language=en-US&page=1`
      const movieRes = await fetch(movieUrl)
      const movieData = await movieRes.json()
      // 返回合并了电影数据的新对象
      return {
        ...genreItem,
        movies: movieData.results.slice(0,20) // 取前20条电影
      }
    })
    // 等待所有请求完成
    const finalMovieData = await Promise.all(movieRequests)
    // 一次性更新state
    setMovieData(finalMovieData)
  } catch (err) {
    console.error(err);
  }
}
  • 方案2:渐进式更新,用户体验更好
    不用等所有请求返回,每个分类的电影加载完成后就单独更新state,用户可以逐步看到内容,不会长时间白屏:
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();
    const formattedGenres = data.genres.map(item => ({
      genreId: item.id,
      genre: item.name,
      movies: []
    }))
    // 先设置初始结构,页面可以先渲染分类标题
    setMovieData(formattedGenres)
    // 逐个发起请求,每个请求完成后单独更新
    formattedGenres.forEach(async (genreItem, index) => {
      try {
        const movieUrl = `https://api.themoviedb.org/3/discover/movie?api_key=xxx&with_genres=${genreItem.genreId}&language=en-US&page=1`
        const movieRes = await fetch(movieUrl)
        const movieData = await movieRes.json()
        // 用函数式更新避免闭包问题
        setMovieData(prev => {
          const newData = [...prev]
          newData[index] = {
            ...newData[index],
            movies: movieData.results.slice(0,20)
          }
          return newData
        })
      } catch (e) {
        console.error(`加载${genreItem.genre}分类电影失败`, e)
      }
    })
  } catch (err) {
    console.error(err);
  }
}

问题3解答:更简单高效的实现方案

上面给出的Promise.all并发请求是目前最常用的高效方案,除此之外还有几个优化方向:

  • 如果分类数量超过10个,可以做分批请求,避免触发TMDB的接口限流
  • 可以给分类电影数据加缓存,下次进入页面不用重复请求相同数据
  • 不想手动处理请求逻辑的话,可以用React Query/SWR这类请求库,自动帮你处理缓存、并发、loading状态,代码会更简洁。

内容的提问来源于stack exchange,提问作者ramuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 16:57:03