You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中如何避免无限循环与API重复调用?

解决方案

问题根源分析

  1. 无限重渲染:你直接在组件渲染流程中调用setSelectedDataFromTabA和setSelectedDataFromTabB,每次setState都会触发组件重新渲染,渲染时又会再次调用setState,形成死循环。
  2. 重复API调用:你的useFetch接收的是getData()这类直接执行的函数调用——组件每次渲染时,getData()都会被执行一次,发起新的API请求。原useFetch的useEffect依赖为空,无法正确控制请求触发时机,加剧了重复调用问题。

分步修复

1. 修复useFetch Hook

修改Hook,让它接收API调用函数本身而非直接执行后的Promise,避免组件渲染时重复发起请求,同时通过依赖控制请求触发时机:

function useFetch(serviceFunction) {
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState(false);
  const [data, setData] = React.useState(null);

  React.useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await serviceFunction(); // 在Hook内部执行API调用函数
        if (response.status !== 200) {
          throw new Error('请求失败');
        }
        setData(response.data);
        setError(false);
      } catch (err) {
        setError(true);
        setData(null);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [serviceFunction]); // 依赖API调用函数,确保函数变化时重新请求

  return { loading, error, data };
}

2. 优化组件逻辑(两种可选方案)

方案一:用useMemo计算派生状态(推荐)

过滤后的数据属于派生状态,不需要存到state里,直接用useMemo基于原始数据计算即可,避免不必要的state更新:

const MyComponent = ({}) => {
  // 传入API调用函数本身,而非执行后的结果
  const { data, loading, error } = useFetch(getData);
  const { data: dataTabA, loading: loadingTabA, error: errorTabA } = useFetch(getDataTabA);
  const { data: dataTabB, loading: loadingTabB, error: errorTabB } = useFetch(getDataTabB);

  // 处理加载和错误状态
  if (loading || loadingTabA || loadingTabB) return <div>loading</div>;
  if (error || errorTabA || errorTabB) return <div>error</div>;

  // 用useMemo缓存过滤结果,仅当依赖变化时重新计算
  const selectedDataFromTabA = React.useMemo(() => {
    return dataTabA.filter(x => x.IdTabA === data[0]?.IdTabA);
  }, [data, dataTabA]);

  const selectedDataFromTabB = React.useMemo(() => {
    return dataTabB.filter(x => x.IdTabB === data[0]?.IdTabB);
  }, [data, dataTabB]);

  return (
    <div>
      {/* 在这里使用selectedDataFromTabA和selectedDataFromTabB */}
    </div>
  );
};

方案二:用useEffect更新state(如果必须存到state)

如果你因为业务需求需要把过滤后的数据存到state里,用useEffect并设置正确的依赖,确保只有当原始数据变化时才更新state:

const MyComponent = ({}) => {
  const [selectedDataFromTabA, setSelectedDataFromTabA] = React.useState([]);
  const [selectedDataFromTabB, setSelectedDataFromTabB] = React.useState([]);

  const { data, loading, error } = useFetch(getData);
  const { data: dataTabA, loading: loadingTabA, error: errorTabA } = useFetch(getDataTabA);
  const { data: dataTabB, loading: loadingTabB, error: errorTabB } = useFetch(getDataTabB);

  if (loading || loadingTabA || loadingTabB) return <div>loading</div>;
  if (error || errorTabA || errorTabB) return <div>error</div>;

  React.useEffect(() => {
    // 仅当data、dataTabA变化时执行过滤并更新state
    setSelectedDataFromTabA(dataTabA.filter(x => x.IdTabA === data[0]?.IdTabA));
  }, [data, dataTabA]);

  React.useEffect(() => {
    setSelectedDataFromTabB(dataTabB.filter(x => x.IdTabB === data[0]?.IdTabB));
  }, [data, dataTabB]);

  return (
    <div>
      {/* 使用state中的数据 */}
    </div>
  );
};

额外注意点

  • 加入可选链data[0]?.IdTabA,避免data[0]为undefined时抛出错误。
  • 如果getData、getDataTabA等函数是在组件内部定义的,记得用useCallback包裹,避免每次渲染生成新函数导致useFetch重复请求。

内容的提问来源于stack exchange,提问作者user10863293

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 10:05:14