页面加载/刷新时组件辅助函数fetchSkuList被调用两次的问题
fetchSkuList() 页面加载时被调用两次的问题 嘿,我来帮你梳理下这个让人困惑的问题——页面加载/刷新时fetchSkuList()被调用两次,大概率是下面这些常见原因导致的,你可以逐一排查:
1. React Strict Mode 的刻意重复调用(最常见于React 18+)
如果你用的是React 18及以上版本,并且项目根组件被<React.StrictMode>包裹着,那在开发环境下,React会刻意重复调用某些函数(包括组件渲染函数、useEffect回调,以及你在渲染阶段执行的fetchSkuList())。这么做是为了帮你提前检测出那些不符合规则的副作用(比如没有清理逻辑的订阅、重复创建的资源等)。
你可以先临时注释掉根组件的<React.StrictMode>标签,刷新页面看看fetchSkuList()的调用次数是不是变成一次。如果是,那就是Strict Mode的原因——这属于开发环境的正常检测行为,生产环境下Strict Mode不会触发这个重复调用,所以上线后不用担心。
2. 组件重复渲染导致函数重复执行
如果fetchSkuList()是直接写在组件的渲染逻辑里(比如在return语句之前执行,或者在JSX中直接调用),那每次组件渲染都会触发它。而组件可能因为以下原因重复渲染:
- 父组件重新渲染,导致子组件跟着渲染
- 组件自身的状态/prop发生了变化(哪怕看起来没变化,但如果是引用类型的prop,比如每次父组件渲染都新建一个数组
const skus = [123, 456],会被React判定为prop变化)
排查方法:
- 在组件函数开头加个日志:
console.log('当前组件渲染了'),看看组件是不是确实渲染了两次 - 打开React DevTools的Profiler工具,录制一次页面加载的过程,查看组件的渲染触发源,能精准定位到是哪个状态/prop变化导致的渲染
- 如果是引用类型prop的问题,用
useMemo缓存父组件传递的SKU数组:const memoizedSkus = useMemo(() => [123, 456], []),再把memoizedSkus传给子组件
3. useEffect 依赖项设置不当
如果你是在useEffect里调用fetchSkuList(),那可能是依赖项的问题导致它触发两次:
- 在React 18的开发环境下,哪怕是空依赖的
useEffect(() => { fetchSkuList() }, []),也会因为Strict Mode被执行两次 - 如果依赖项数组里有一个每次渲染都会变化的值(比如没有用
useCallback缓存的函数、没有用useMemo缓存的对象),也会导致useEffect重复触发
排查方法:
- 先确认是不是Strict Mode的原因(参考第一条)
- 检查
useEffect的依赖项,确保所有依赖都是稳定的——比如用useCallback缓存fetchSkuList本身(如果它是在组件内部定义的):const fetchSkuList = useCallback(() => { // 你的函数逻辑 console.log('调用fetchSkuList') }, []) // 这里添加函数依赖的变量 useEffect(() => { fetchSkuList() }, [fetchSkuList])
4. 路由/页面挂载逻辑重复
如果你的组件是路由组件,可能存在路由重复匹配或者页面被多次挂载的情况:
- 路由配置里有两个不同的路径匹配到了同一个组件
- 某些路由守卫、导航逻辑导致组件被卸载后又立即重新挂载
排查方法:
在组件里加个挂载/卸载的日志,看看组件的生命周期:
useEffect(() => { console.log('组件挂载成功') return () => { console.log('组件被卸载') } }, [])
如果刷新页面时出现“组件挂载成功”→“组件被卸载”→“组件挂载成功”的日志,那就是组件被重复挂载了,需要检查路由配置和导航逻辑。
内容的提问来源于stack exchange,提问作者Antonio

