React 18组件mount-unmount-remount异常:useEffect执行两次问题求助
React 18中useEffect在mount阶段执行两次的解决方案
问题原因
这是React 18在开发环境下Strict Mode的新增行为:为了帮开发者提前排查未正确清理的副作用(比如未取消的订阅、未终止的请求等),Strict Mode会模拟组件「挂载→卸载→重新挂载」的过程,导致空依赖的useEffect执行两次。生产环境下不会触发该行为,useEffect仍只会执行一次。
解决方案
方案1:临时关闭Strict Mode(不推荐长期使用)
如果只是开发阶段想快速还原React 17的行为,可修改项目入口文件(通常是index.js或main.jsx),移除<StrictMode>的包裹:
// React 18默认入口代码 import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> ); // 修改后代码 import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<App />);
方案2:让副作用支持重复执行(推荐)
正确的做法是适配React 18的并发特性,确保副作用代码能被正确清理,即使执行多次也不会产生异常。以API请求场景为例,使用AbortController取消未完成的请求:
import { useEffect, useState } from "react"; import "./styles.css"; export default function App() { const [apiCalled, setApiCalled] = useState(0); useEffect(() => { const controller = new AbortController(); const signal = controller.signal; const callApi = async () => { try { // 模拟API请求逻辑 await new Promise(resolve => setTimeout(resolve, 100)); // 仅在请求未被取消时更新状态 if (!signal.aborted) { setApiCalled(prev => prev + 1); } } catch (error) { // 忽略取消请求的错误 if (error.name !== 'AbortError') { console.error('API请求失败:', error); } } }; callApi(); // 组件卸载时终止请求 return () => controller.abort(); }, []); return <div className="App">Api Called: {apiCalled}</div>; }
这样即使Strict Mode触发两次挂载,第一次的请求会被主动终止,最终只会更新一次状态,符合预期。
内容的提问来源于stack exchange,提问作者Yatin Gupta
相关产品推荐
相关产品推荐

