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

Posted

技术标签:

【中文标题】在反应中获取多个网址时无法更新钩子变量【英文标题】:unable to update hooks variable when fetching multiple urls in react 【发布时间】:2021-03-31 02:18:05 【问题描述】:

我正在尝试使用自定义反应挂钩存储来自 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>
  );

我什么也得不到,没有电影放映。非常感谢您的帮助。

【问题讨论】:

【参考方案1】:

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);

【讨论】:

我刚刚添加了使用 Promise.all 的代码。 Wong 在你的例子中,我正确地获取了电影。现在我正在尝试显示它们 我使用了 Promise.all 方法,效果很好,谢谢!【参考方案2】:

试试这个

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();

  ,[]);

【讨论】:

不起作用 TypeError: response[0].concat is not a function 我现在使用的示例与此不同。 代码已更新..请检查..如果它不起作用请告诉我 使用你下面的例子我设法得到了电影,现在我正试图正确地显示它们。非常感谢您抽出宝贵的时间。【参考方案3】:

您没有从useFetch 收到任何值的原因(假设有要返回的值)是您返回结构 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  src=path />
                        <h4 className="movie-overview">movie.overview</h4>
                        <p className="movie-title">movie.title</p>
                    </div>
                )
            
        </div>
    );

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

【讨论】:

那只是一个错字,我更改了我的代码。我现在返回 movieTypes 问题仍然存在

以上是关于在反应中获取多个网址时无法更新钩子变量的主要内容,如果未能解决你的问题,请参考以下文章

OnSubmit 函数不会更新 React 中的反应钩子状态变量

反应:带有自定义钩子的滑块无法正常工作

如何使用反应钩子 react.js 一次更新多个状态

动态表单 - 如何使用反应钩子更新“onChange”事件中多个表单字段的值?

使用上下文和钩子更新未安装组件的状态 - 反应原生

反应 useState 钩子不使用 axios 调用更新