在反应中获取多个网址时无法更新钩子变量

unable to update hooks variable when fetching multiple urls in react

我正在尝试使用自定义反应挂钩存储来自 TMDB 电影的数据 api。

useFetch.js

import {React,useState,useEffect} from 'react';


export default function useFetch() {

  const key = process.env.REACT_APP_API_KEY;
  
  //the urls I want to get data from     
  const specificTypes = [
    {'Type':'Top Rated', 'url' :`https://api.themoviedb.org/3/movie/top_rated?api_key=${key}`},
    {'Type':'Trending', 'url' :`https://api.themoviedb.org/3/trending/movie/day?api_key=${key}`},
  ];

 

  const [movieTypes,setMovieTypes] = useState([]); //this is where I want to store url data 

  useEffect(()=>{
    
    const fetchSpecificTypes = async ()=>{
      
       const promises = [];
       specificTypes.map(async type=>{
         let response = await fetch(type.url);
         let res = await response.json();
         promises.push(res.results);
       });
       console.log({promises}); data is stored in promises successfully
       setMovieTypes(promises); //how I try to set the movies 


    } 

    fetchSpecificTypes();

  },[]);

  return {movieTypes};

}

当我 console.log({promises}) 我得到这个对象,其中 2 项是电影类型,里面有 20 部电影: 然后当我尝试在另一个组件中显示上面对象中的电影时:

MovieList.js

    import {React , useState,useEffect} from 'react'
    import useFetch from './useFetch';
    import '../App.css';
    
    export default function MovieList() {
  
  const {movieTypes} = useFetch();
  const baseImgUrl = "https://image.tmdb.org/t/p";
  const size = "/w400";
  

  return (
    <div className="movie-list">
      {
        movieTypes.map(movie=>{
          return (
            <>
              {
                Object.values(movie).map((val,k)=>{
                    let path = baseImgUrl + size + val.backdrop_path; //full image path
                    return <div className= "movie-item" key = {val.id}>
                              <img alt = "movie" src = {path}/>
                              <h4 className="movie-overview"> {val.overview} </h4>
                              <p className="movie-title">{val.title}</p>
                    </div>
                })
              }
            </>
          )
        })  
      }
    </div>
  );
}

我什么都没有,没有电影显示。非常感谢您对此的帮助。

Await 在 Array.map() 中未按预期工作。 您应该使用 for 循环或使用 Promise.all()

const fetchSpecificTypes = async() => {
  const promises = [];
  for(let i=0; i<specificTypes.length; i++) {
    let response = await fetch(specificTypes[i].url);
    let res = await response.json();
    promises.push(res.results);
  }
  setMovies(promises);
}

const fetchSpecificTypes = async() => {
  const results = await Promise.all(specificTypes.map(type => fetch(type.url)));
  const results2 = await Promise.all(results.map(res => res.json());
  const movies = results2.map(res => res.results);
  setMovies(movies);
}

试试这个

useEffect(()=>{
    
    const fetchSpecificTypes = async ()=>{
       const promises=specificTypes.map(type=>fetch(type.url).then(res=>res.json()));
       const response=await Promise.all(promises)
       setMovies(response.flatMap(c=>c)); 
    } 

    fetchSpecificTypes();

  },[]);

您没有从 useFetch 收到任何值的原因(假设 return 有值)是因为您 return 结构 { movies: [...] } 但您阅读了不存在的成员 movieTypes.

改变

const {movieTypes} = useFetch();

const {movies: movieTypes} = useFetch();

在 comp 之外的函数中声明组件状态和效果在我看来非常可疑。为了清晰和可能的功能,我认为最好在组件的顶层声明状态和任何效果。 (如果需要,效果的实现而不是声明可以拆分出来重用。)例如,组合组件:

import { React, useState, useEffect } from 'react'
import '../App.css';

export default function MovieList() {
    const key = process.env.REACT_APP_API_KEY;
    const specificTypes = [
        { 'Type': 'Top Rated', 'url': `https://api.themoviedb.org/3/movie/top_rated?api_key=${key}` },
        { 'Type': 'Trending', 'url': `https://api.themoviedb.org/3/trending/movie/day?api_key=${key}` },
    ];

    const [movieTypes, setMovieTypes] = useState([]);

    useEffect(() => {
        const fetchSpecificTypes = async () => Promise.all(specificTypes.map(type => fetch(type.url)
            .then(r => r.json())
            .then(r => r.results)
        )).then(results => {
            console.log(results);
            setMovieTypes([].concat(...results))
        }).catch(e => console.error(e));

        fetchSpecificTypes();
    }, []);

    const baseImgUrl = "https://image.tmdb.org/t/p";
    const size = "/w400";
    return (
        <div className="movie-list">
            {
                movieTypes.map(movie => {
                    let path = baseImgUrl + size + movie.backdrop_path;
                    return <div className="movie-item" key={movie.id}>
                        <img alt="movie" src={path} />
                        <h4 className="movie-overview">{movie.overview}</h4>
                        <p className="movie-title">{movie.title}</p>
                    </div>
                })
            }
        </div>
    );
}

请注意,提取请求已重组以传播承诺,而不是重复等待承诺。当所有的获取承诺都解决时,数组被展平并设置状态变量。如果发生错误,则会报告。我没有 运行 代码,但我认为它抓住了这个想法。