React中useEffect触发两次求助:组件渲染后API调用执行两次
解决useEffect触发两次API调用的问题
常见原因及对应方案
1. React 18 开发环境的Strict Mode特性
React 18在开发模式下的Strict Mode,会刻意重复执行挂载阶段的副作用(包括useEffect),目的是帮你提前发现代码里的潜在问题,比如未清理的订阅、重复创建的实例等。这种情况只在开发环境出现,生产环境不会触发两次,属于正常现象。如果想临时在开发环境屏蔽这个特性,可以移除项目根组件外的StrictMode包裹,但不推荐长期这么做——保留它能帮你提前规避很多隐患。
2. 依赖项openBanner引用不稳定
你的useEffect依赖数组里加了openBanner,如果这个函数是在组件内部定义的,又没用到useCallback包裹,那每次组件渲染时都会生成一个新的openBanner函数实例。React会认为依赖项变了,于是重新执行useEffect,导致API调用两次。
解决方法很简单:用useCallback把openBanner包起来,固定它的引用:
import { useCallback } from 'react'; // 假设openBanner是组件内定义的函数 const openBanner = useCallback((message, type) => { // 这里写你的banner提示逻辑 }, []); // 根据函数内部用到的变量,添加对应的依赖项
之后useEffect就只会在openBanner的实际依赖变化时才重新执行,不会因为函数引用变化触发重复调用。
3. 组件被意外重复挂载
检查组件是不是被父组件频繁重新渲染导致卸载再挂载,或者在条件渲染、路由切换中被重复创建。可以加一段日志排查:
useEffect(() => { console.log("组件挂载"); return () => console.log("组件卸载"); }, []);
如果控制台交替出现"组件卸载"和"组件挂载",说明组件确实被重复挂载了,得去排查父组件的渲染逻辑或者路由配置。
调整后的示例代码
顺便优化下你的代码逻辑,同时确保依赖稳定:
import { useEffect, useCallback } from 'react'; // 先给openBanner加useCallback const openBanner = useCallback((message, type) => { // 你的banner实现逻辑 }, []); useEffect(() => { console.log("getting all pipelines"); const api = new Api(); api.get_pipelines() .then(response => { // 把排序逻辑单独提出来,更清晰 const sortedPipelines = response.data.sort((a, b) => { if (a.pipelineState > b.pipelineState) return 1; if (b.pipelineState > a.pipelineState) return -1; return 0; }); getPipelines(sortedPipelines); }) .catch(error => { console.log("error getting existing pipelines", error); // 加个可选链,避免error.response不存在时报错 if (error.response?.status === 500 || error.response?.status === 404) { openBanner("Cannot load existing pipelines", "error"); } }); }, [openBanner, getPipelines]); // 注意:如果getPipelines是useState返回的setter,它本身是稳定的,可以不用加进依赖;如果是自定义函数,同样要用useCallback包裹
内容的提问来源于stack exchange,提问作者mm1975
相关产品推荐
相关产品推荐

