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

React新手求助:无依赖useEffect多次调用API问题及替代方案

useEffect空依赖却多次调用API的原因与解决方案

原因分析

  1. React严格模式(Strict Mode)的开发环境行为
    在开发环境下,React Strict Mode会主动重复调用副作用函数(包括useEffect)、组件构造函数等,目的是帮你检测潜在bug——比如未清理的订阅、内存泄漏、重复请求导致的逻辑问题。这种重复调用只会在开发环境出现,生产环境不会触发。

  2. 组件重复挂载
    如果父组件(/pages/dashboard/speaking/[slug].js)因为slug、userData等变量频繁变化导致重新渲染,可能会让StudentRepeatSentece组件被卸载后重新挂载,进而触发useEffect再次执行。

  3. 代码笔误引发的潜在问题
    你的StudentRepeatSentece组件里存在笔误:定义的状态更新函数是setPara,但实际调用了不存在的setData,这会导致para状态始终未被更新,可能引发组件不必要的重复渲染,甚至后续逻辑异常。

可行解决方案

方案1:用状态标记避免重复请求

添加一个isFetched状态,确保API只被调用一次:

export function StudentRepeatSentece(params) {
    const [para, setPara] = useState();
    const [isFetched, setIsFetched] = useState(false);

    useEffect(() => {
         // 已经请求过就直接返回
         if (isFetched) return;

         const handlePara = async () => {
              try {
                  console.log(">>>>>>>>>>>>>>>>fetching Have Been Start");
                  const res = await fetch("/api/speaking/read-aloud");
                  const data = await res.json();
                  setPara(data.para); // 修正笔误:setData改为setPara
                  setIsFetched(true);
              } catch (err) {
                  console.error("请求失败:", err);
              }
         };

      handlePara();
   }, [isFetched]);

   return (<> <SecondComponent myPara={para}/> </> );
}

方案2:用AbortController取消重复请求

针对Strict Mode的重复调用,使用AbortController取消第一次未完成的请求,避免无效请求:

export function StudentRepeatSentece(params) {
    const [para, setPara] = useState();

    useEffect(() => {
         const controller = new AbortController();
         const signal = controller.signal;

         const handlePara = async () => {
              try {
                  console.log(">>>>>>>>>>>>>>>>fetching Have Been Start");
                  // 把signal传入fetch,用于取消请求
                  const res = await fetch("/api/speaking/read-aloud", { signal });
                  const data = await res.json();
                  setPara(data.para); // 修正笔误
              } catch (err) {
                  // 忽略取消请求的错误
                  if (err.name !== 'AbortError') {
                      console.error("请求失败:", err);
                  }
              }
         };

      handlePara();

      // 组件卸载或副作用重新执行时,取消请求
      return () => controller.abort();
   }, []);

   return (<> <SecondComponent myPara={para}/> </> );
}

方案3:避免组件不必要的重复挂载

在父组件中用useMemo缓存渲染结果,只有当userData.role或slug变化时才重新生成组件:

} else if(slug == "repeat-sentence") {
    const TargetComponent = useMemo(() => {
        if (userData.role == "Admin") {
            return <AdminRepeatSentence />;           
        } else if(userData.role == "Teacher") {
            return <TeacherRepeatSentece />;
        } else if(userData.role == "Student") {
            return <StudentRepeatSentece />;            
        }
    }, [userData.role, slug]); // 依赖项:只有这两个值变了才重新渲染

    return TargetComponent;

额外修正:修复状态更新的笔误

一定要把StudentRepeatSentece里的setData(data.para)改成setPara(data.para),否则para状态永远不会更新,子组件SecondComponent也拿不到正确的myPara值。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 09:30:58