如何阻止多个重新渲染执行多个 api 调用 useEffect?

Posted

技术标签:

【中文标题】如何阻止多个重新渲染执行多个 api 调用 useEffect?【英文标题】:how to stop multiple re-renders from doing multiple api calls useEffect? 【发布时间】:2021-09-13 12:24:17 【问题描述】:

我是响应功能组件的新手,我正在尝试在页面加载时获取多个城市的天气数据,但 useEffect 现在正在重新呈现每个调用。如何编写这样 useEffect 不会导致重新渲染?

function App() 
    const [data, setData] = useState([]);
    const [activeWeather, setActiveWeather] = useState([]);

    useEffect(() => 
        const key = process.env.REACT_APP_API_KEY;

        const fetchData = async (city) => 
            const res = await axios.get(`https://api.openweathermap.org/data/2.5/weather?q=$city&appid=$key`);
            setData((data) => [
                ...data,
                 description: res.data.weather[0].description, icon: res.data.weather[0].icon, temp: res.data.main.temp, city: res.data.name, country: res.data.sys.country, id: res.data.id ,
            ]);
        ;
        const fetchCities = () => 
            const cities = [fetchData("Ottawa"), fetchData("Toronto"), fetchData("Vancouver"), fetchData("California"), fetchData("London")];

            Promise.all(cities).catch((err) => 
                console.log(err);
            );
        ;
        fetchCities();
    , []);

【问题讨论】:

【参考方案1】:

您可以让fetchData 函数在不更新状态的情况下返回您需要的数据,然后您可以获取 x 数量的城市,并且仅当所有请求完成更新状态时。

请注意,如果Promise.all 内部的请求之一失败,它将转到catch 块而不返回任何数据,基本上全有或全无

const key = process.env.REACT_APP_API_KEY

const fetchCity = async city => 
  const  data  = await axios.get(
    `https://api.openweathermap.org/data/2.5/weather?q=$city&appid=$key`,
  )

  return 
    description: data.weather[0].description,
    icon: data.weather[0].icon,
    temp: data.main.temp,
    city: data.name,
    country: data.sys.country,
    id: data.id,
  


function App() 
  const [cities, setCities] = useState([])
  const [activeWeather, setActiveWeather] = useState([])

  useEffect(() => 
    const fetchCities = async () => 
      const citiesData = await Promise.all(
        ['Ottawa', 'Toronto', 'Vancouver'].map(fetchCity),
      )

      setCities(prevState => prevState.concat(citiesData))
    

    fetchCities()
  , [])

【讨论】:

这是一个很好的答案!旁注:关于全有或全无方面的观点是正确的(可以使用allSettled 获得更多细粒度的信息),但是重新 "...它将进入 catch 块而不解决任何问题的承诺..." Promise.all解决除它自己之外的任何承诺,无论Promise.all 做什么,承诺都会解决。 Promise.all 所做的只是观察结果并履行或拒绝自己的承诺。 (关于“解决”与“履行”与“解决”,我did a blog post。) @T.J.Crowder 是的好点,将更新答案。 (并在途中阅读帖子)【参考方案2】:

您可以使用Promise.all,然后调用setData 一次。像这样:

useEffect(() => 
  const fetchCity = (city) => axios.get(`$base/$city`);
  const cities = ["Ottawa", "Toronto"];
  const promises = cities.map(fetchCity);
  Promise.all(promises).then((responses) => 
    setData(cities.map((city, index) => ( city, ...responses[index] )));
  );
, []);

【讨论】:

以上是关于如何阻止多个重新渲染执行多个 api 调用 useEffect?的主要内容,如果未能解决你的问题,请参考以下文章

React Hooks:跳过多个连续 setState 调用的重新渲染

如何运行多个异步函数然后执行回调?

如何停止在 React 中重新渲染时进行 api 调用?

React:如何停止重新渲染组件或对同一路由更改路由器进行 API 调用

如何从多个 API 调用更新熊猫数据框

如何并行调用多个 API 进行负载测试(使用 Gatling)?