【问题标题】:how to stop multiple re-renders from doing multiple api calls useEffect?如何阻止多个重新渲染执行多个 api 调用 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();
    }, []);

【问题讨论】:

    标签: javascript reactjs axios react-hooks react-functional-component


    【解决方案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] })));
      });
    }, []);
    

    【讨论】:

      猜你喜欢
      • 2020-07-06
      • 2019-11-21
      • 2021-05-03
      • 2020-04-23
      • 2022-01-16
      • 1970-01-01
      • 2020-10-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多