为何useEffect会触发两次?React useEffect执行异常问询
为什么第二个useEffect会触发两次?
你的代码中第二个useEffect触发两次,主要有两种常见原因,对应不同场景:
原因1:初始依赖值触发执行
第二个useEffect的依赖是listVisaoGeral,组件初始渲染时,listVisaoGeral的初始值是[],此时React会执行这个useEffect一次。当第一个useEffect完成数据请求并调用setListVisaoGeral更新state后,listVisaoGeral从空数组变为接口返回的新数组,依赖项发生变化,触发第二次执行。
这种情况是React的正常行为,两次执行分别对应:
- 组件挂载时的初始state
- state更新后的新值
原因2:React 18 Strict Mode(开发环境)
如果你的项目开启了Strict Mode(Create React App、Vite等脚手架默认开启),React在开发环境下会模拟组件的卸载和重新挂载,刻意重复执行useEffect、state初始化等逻辑,目的是帮你检测未正确清理的副作用(比如未取消的请求、未清除的定时器)。
这会导致:
- 第一个空依赖的useEffect会执行两次,若请求没有缓存,会触发两次接口调用,进而两次更新state,导致第二个useEffect多一次执行;
- 即使请求有缓存,state只更新一次,Strict Mode也会让依赖state变化的useEffect执行两次。
解决方案
1. 跳过初始空数组的执行
如果你不需要在初始state(空数组)时执行第二个useEffect,可以添加判断过滤:
useEffect(() => { // 跳过初始空数组的情况 if (listVisaoGeral.length === 0) return; console.log("Second"); }, [listVisaoGeral]);
2. 为请求添加清理逻辑(应对Strict Mode)
为了避免Strict Mode下重复请求的问题,给axios请求添加取消逻辑,确保组件卸载时能终止未完成的请求:
const [listVisaoGeral, setListVisaoGeral] = useState([]); useEffect(() => { const abortController = new AbortController(); console.log("First"); async function getVisaoGeral() { try { const response = await axiosInstance.get("....", { signal: abortController.signal }); setListVisaoGeral(response.data); } catch (error) { // 忽略取消请求的错误 if (error.name !== "AbortError") { console.log(error); Alert.alert( "Erro", "Não foi possível consultar a lista de recursos. Por favor, tente novamente." ); } } } getVisaoGeral(); // 组件卸载时取消请求 return () => abortController.abort(); }, []); useEffect(() => { console.log("Second"); }, [listVisaoGeral]);
3. 临时关闭Strict Mode(仅调试用)
如果只是想验证是否是Strict Mode导致的问题,可以暂时移除项目入口文件(如index.js)中的<StrictMode>标签。注意:生产环境下Strict Mode会自动失效,不建议长期关闭,它能帮你提前发现潜在问题。
内容的提问来源于stack exchange,提问作者Thiago Costa
相关产品推荐
相关产品推荐

