【React】 严格模式的 “双重执行” 机制,useEffect 执行两次
在开发环境中,组件被<StrictMode>包裹时,React会对副作用函数(例如 useEffect)进行两次执行,具体表现为:
- 第一次执行副作用(触发 useEffect 回调);
- 立即“模拟销毁”组件(执行 useEffect 的清理函数,如果有的话);
- 再次执行副作用(第二次触发 useEffect 回调)
机制目的:
- 检测副作用中是否存在“未清理的资源”(如 未取消的定时器、事件监听等),提前暴露潜在的内存泄漏问题;
- 确保副作用逻辑时“幂等的”(多次执行不会产生意外副作用,比如重复发请求、重复创建实例等);
如何验证
检查入口文件(通常是 index.tsx 或 main.tsx),确认是否有 <StrictMode> 包裹:
// 开发环境下的典型配置
ReactDOM.createRoot(document.getElementById('root')).render(<React.StrictMode> {/* 这里是关键 */}<App /></React.StrictMode>
);暂时移除<StrictMode>后重新运行,此时 useEffect 只会执行一次;
需要处理吗?
- 开发环境:无需处理,这是 React 帮助开发者排查问题的机制,本身不会影响功能逻辑(只要副作用是幂等的,比如接口多次调用结果一致);
- 生产环境:严格模式会自动失效(React在生成环境不会执行双重渲染 / 副作用),因此上线后useEffect 只会执行一次,无需担心;
某些api调用两次,是开发环境下 React 严格模式的正常行为,目的是检测副作用的健壮性。只要接口调用是幂等的(多次调用无副作用),就无需修改代码,若想在开发中避免,可以暂时移除严格模式(但不推荐,严格模式能帮你提前发现问题)。
如果全局搜索并没有搜到 StrictMode?
1、可能是React 18+ 的自动批处理(Auto Batching)与并发渲染(React 18+开发环境的“严格模式+并发渲染”叠加效应)
React 18 引入了自动批处理和并发渲染机制,在开发环境中,即使没有 StrictMode,某些场景下(如组件初始化,状态更新)可能触发额外的“预备渲染”,导致 useEffect 执行两次:
- 并发渲染会在后台“预计算”渲染结果,可能导致组件在初始化时先进行一次“临时渲染”,再进行一次“实际渲染”,从而触发 useEffect 两次;
- 这种行为是 React 18 为了优化渲染性能引入的,仅在开发环境存在,生产环境会合并渲染,只执行一次;
2、父组件或上下文(Context)的强制更新
如果当前组件依赖的父组件或上下文(Context)在初始化时发生了两次状态更新,会导致子组件被强制重新渲染,进而触发 useEffect 两次:
- 例如父组件中使用 useState 初始化时,连续调用了两次 setState(即使值相同,也可能触发更新);
- 验证方式:在父组件中添加日志,观察是否有多次渲染:
// 父组件中
console.log('父组件渲染'); // 若打印两次,说明子组件可能被连带渲染两次