如何使用 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 更新状态?

useState 钩子一次只能设置一个对象,并将同一个数组的另一个对象返回到初始状态

在 useState 钩子中反应设置状态

如何在不删除旧状态的情况下设置对象的状态数组

如何使用我的反应应用程序中的 useState 钩子给出的 setState 方法增加状态对象的选定索引的计数

如何使用 Typescript 和 useState 将类型对象设置为 React 的初始值?