【问题标题】:wait for multiple API calls to finish using useAxios custom hook使用 useAxios 自定义钩子等待多个 API 调用完成
【发布时间】:2022-01-05 21:26:30
【问题描述】:

我使用 useAxios 自定义挂钩来避免代码重复,因为我调用的 API 始终相同。

import { useState, useEffect } from 'react';
import rest from './rest';     // inside there is a definition of axios.create baseURL...       

const useAxios = (endpoint,queryString) => {

  const [axiosData, setAxiosData] = useState(null);
  const [axiosLoading, setAxiosLoading] = useState(true);

  useEffect(() => {

    const getRest = async () => {
      setAxiosLoading(true);
      try{
        const sensors  = await rest
        .get('/'+ endpoint + '?' + queryString)
        .then(res => {
          setAxiosData(res.data);
          setAxiosLoading(false);
        });
      }catch (e) {
        console.log(e);
      }
    }

    getRest(); 

  }, [endpoint,queryString])

  return {axiosData, axiosLoading}; 

}
export default useAxios;

这是我调用useAxios钩子的部分代码。

const {axiosData:data1} = useAxios('request1','');
const {axiosData:data2} = useAxios('request2','querystring1');
const {axiosData:data3} = useAxios('request3','querystring2');

我要解决的问题是如何等待所有请求完成,这样就可以肯定所有数据都准备好了。

我知道axios.all 并想使用类似的东西,但要使用可重用的代码,例如useAxios

旁注:通过应用程序有很多 useAxios 调用,而不仅仅是我在这里解释的一种情况。这就是我试图重用它而不是使用 axios.all

的原因

编辑: 我之前可能已经提到过,但在子组件中还有另一个 useAxios (data4) 调用,它使用来自 data1, data2, data3 的数据,并且应该渲染另一个子组件的次数与 data1 中包含的对象数量一样多。我正在通过 Context API 传递数据,需要确保 data1, data2, data3useAxios (data4) 调用之前可用

【问题讨论】:

    标签: reactjs react-hooks axios


    【解决方案1】:

    显然你可以做一些简单的事情

    if (!data1 && !data2 && !data3) {
      // do something
    }
    

    因为该代码将在这些统计变量之一发生更改时运行。或者您可以将它们全部作为 useEffect() 的依赖项。

    我怀疑如果事情变得复杂,人们可能会引导您使用状态机。见useReducer()

    根据新信息更新 您可能知道,每次都必须以相同的顺序调用挂钩。你写这个问题是因为你正在处理代码异味,我怀疑我能给出的最“正确”的答案是考虑如何重构代码。把事情分解成更小的单位,让每个单位承担更少的责任。

    “快速创可贴”是更新 useAxios(),这样你就可以不操作它了:

    const useAxios = (endpoint,queryString,bornReady=true) => {
    ...
    if (bornReady) {
      getRest(); 
    }
    ...
    

    通过将新参数默认为 true,旧代码将继续运行,但您可以使用该标志来阻止挂钩运行。在这里,我也使用查询字符串作为标志。当您可以构建查询字符串时,您就可以进行其余的调用了。在此之前,什么都不做。

    const buildQuery = (data, moreData, soMuchData) => {
      if (!data || !moreData || !soMuchData) {
        return undefined // no queryString for you
      }
      // build and return your query
      return `c=${data.custId}&p=${moreData.numThings}`
    }
    const {axiosData:data1} = useAxios('request1','');
    const {axiosData:data2} = useAxios('request2','querystring1');
    const {axiosData:data3} = useAxios('request3','querystring2');
    const querystring3 = buildQuery(data1, data2, data3)
    const {axiosData:data4, axiosLoading} = useAxios('request4',querystring3, !!querystring3);
    

    如果可以,请进行重构。甚至可能将这些调用移至服务器,这样您就可以发出 1 个请求,只返回您需要的数据。如果您陷入困境,请按照上面的说明添加到混乱中。

    【讨论】:

    • data1data2 等将始终为真,因此条件将始终评估为 true
    • @jsejcksn 不会 data1data2 等为空?
    • @SangeetAgarwal ?‍♂️ 你是对的。我没有注意到它们被解构了(我以为它们是从钩子返回的对象。)
    • @Dave 如果我将它们作为 useEffect 的依赖项,那不会运行三遍吗?基本上我需要将它们传递给 DataContext 并且需要确保它们是完整的
    • 请看编辑,它更深入地解释了真实案例。对此感到抱歉
    【解决方案2】:

    不要从钩子返回的每个对象中解构axiosData 属性。这样,您可以检查每个 dataN 值上的 axiosLoading 属性,以确定它们是否全部完成,如下所示:

    const data1 = useAxios('request1','');
    const data2 = useAxios('request2','querystring1');
    const data3 = useAxios('request3','querystring2');
    
    const done = [data1, data2, data3].every(({axiosLoading}) => !axiosLoading);
    if (done) {
      // they're all done loading
    }
    else {
      // at least one is still loading
    }
    

    【讨论】:

      猜你喜欢
      • 2022-01-23
      • 1970-01-01
      • 2013-11-23
      • 1970-01-01
      • 2019-03-25
      • 2018-06-12
      • 2023-03-16
      • 1970-01-01
      • 2016-06-29
      相关资源
      最近更新 更多