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

React StrictMode下useCallback函数在useEffect中重复调用问题

核心结论先讲清楚

useCallback 完全无法避免StrictMode下的useEffect重复执行,你对useCallback的作用存在误解。


为什么会出现两次调用

React 18及以上版本中,开发环境开启<React.StrictMode>时,会对所有新挂载的组件执行挂载→模拟卸载→重新挂载的流程,专门用来检测代码中是否存在未清理的副作用、逻辑不幂等的问题。

  • useCallback加空依赖的作用,是让fetchData函数在组件多次重渲染时保持引用不变,避免因为函数引用变化触发effect重新执行,但它管不到StrictMode主动触发的组件重挂载流程。
  • 哪怕你把useEffect的依赖直接写成空数组[],只要在开发环境的StrictMode下,挂载阶段的effect依然会被执行两次,这是React刻意设计的开发态校验逻辑,生产环境构建时这个行为会自动关闭,线上不会出现两次调用的问题。

随机数据导致非预期副作用的解决方法

这个双调用不是bug,是React在帮你提前暴露代码问题:如果你的副作用逻辑在执行两次时会出问题,说明你没有做副作用清理,上线后遇到用户快速切换路由、组件条件渲染反复挂载的场景,一样会触发同类bug。针对你返回随机数据的场景,可以按下面的方案修复:

  • 给请求加中断清理逻辑:在useEffect的清理函数中,中断上一次未完成的请求,避免无效请求的结果覆盖有效状态,参考实现:
useEffect(() => {
  const abortController = new AbortController();
  // 请求时携带abortSignal,支持中断
  const loadData = async () => {
    const res = await fetch('/your-random-data-api', {
      signal: abortController.signal
    });
    // 只有请求未被中断时,才把结果写入状态
    if (!abortController.signal.aborted) {
      const data = await res.json();
      setQuizData(data);
    }
  };
  loadData();
  // 组件卸载(包括StrictMode模拟的卸载)时中断请求
  return () => abortController.abort();
}, [fetchData]);
  • 如果随机数据是前端本地生成的,不要把生成逻辑放在useEffect里,可以用useState的初始化函数、或者useMemo在组件首次初始化时只生成一次,避免重复执行生成逻辑。
  • 不要为了避开这个检测直接移除<React.StrictMode>,它能帮你在开发阶段提前发现很多隐蔽的副作用问题,远比对两次调用做特殊规避有价值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 10:36:38