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
相关产品推荐
相关产品推荐

