为何React中放在useEffect外的SideEffect执行次数会翻倍?
问题原因解析
这个现象是React 18+ 开发环境下严格模式(<StrictMode>)的刻意设计,结合两段代码的执行阶段差异导致的:
1. 两段代码的执行阶段差异
- 写在
useEffect回调内的代码:属于React约定的提交阶段副作用,只会在DOM更新完成后执行,不属于渲染函数本身的逻辑。 - 写在组件顶层(useEffect外)的代码:属于渲染阶段逻辑,组件每触发一次渲染流程,这段代码就会随着组件函数的调用直接执行。
2. 严格模式的双调用规则
React 18及以上版本,官方脚手架(CRA、Vite等)默认会在项目根组件外层包裹<React.StrictMode>,开发环境下会做两个特殊处理,用来提前排查不纯的渲染逻辑导致的bug:
- 所有渲染流程(包括初始挂载、状态更新触发的重渲染)都会让组件函数被连续调用两次
- 初始挂载时会模拟「组件卸载 -> 重新挂载」的流程,让useEffect回调执行两次
对应两种场景的表现说明
副作用放在useEffect内符合预期的原因
没有传依赖数组的useEffect,会在每次组件渲染提交完成后执行。严格模式下只有初始挂载时会因为模拟重挂载执行两次effect,后续点击按钮触发重渲染时,effect只会执行一次,所以你感知到的效果是每次点击r自增1,符合预期。
副作用放在useEffect外每次自增2的原因
自增逻辑放在组件函数顶层,属于渲染阶段逻辑,严格模式下每一次渲染都会让组件函数被调用两次,所以每触发一次渲染,r就会被自增2次,也就是你看到的不符合预期的现象。
补充验证
你可以把项目根目录下根组件外层的<React.StrictMode>包裹去掉,重新运行后两个场景的表现就会完全一致,每次点击r都仅自增1。另外这个双调用行为仅存在于开发环境,生产环境打包后会自动关闭,不会影响线上表现。
内容的提问来源于stack exchange,提问作者Mahbubur Rahman
相关产品推荐
相关产品推荐

