React子组件useEffect中API调用刷新时触发两次问题排查
问题分析与解决方案
问题根源
当前API调用触发两次的原因主要有两个:
- 刷新按钮的两次
setMyTabs调用,导致子组件先被卸载再重新挂载,触发了挂载时的useEffect执行。 - 若处于React开发环境的严格模式,框架会额外重复执行一次
useEffect用于检测副作用,进一步加剧了重复调用的问题。
原逻辑依赖组件挂载触发API,这种方式在组件频繁卸载/挂载时会不符合需求,我们需要改为主动控制API的触发时机。
推荐解决方案:用刷新标识触发API
不需要通过卸载/挂载组件来触发刷新,而是给子组件传递一个刷新标识,当标识变化时主动触发API调用。
父组件修改
import { useState } from 'react'; // 新增刷新标识状态 const [refreshKey, setRefreshKey] = useState(0); const defaultTabs = { section1: false, section2: false, section3: false }; // 修改刷新按钮的点击逻辑 const handleRefresh = () => { // 只需更新刷新标识,无需切换tab的显示状态 setRefreshKey(prev => prev + 1); }; // 父组件JSX中传递refreshKey给子组件 {myTabs.section2 && ( <div> <MySection2Component hasLoaded={hasLoaded} updateDateTime={updateDateTime} refreshKey={refreshKey} {...props} /> </div> )}
子组件修改
import { useState, useEffect } from 'react'; const MySection2Component = (props) => { const [mySection2Obj, setMySection2Obj] = useState({}); const [isChildLoading, setIsChildLoading] = useState(true); // 补全加载状态的定义 // 依赖refreshKey触发API,首次加载和刷新时都会执行 useEffect(() => { makeApiCall(); }, [props.refreshKey]); useEffect(() => { props.hasLoaded(!isChildLoading); }, [isChildLoading, props.hasLoaded]); // 添加props依赖避免React警告 async function makeApiCall() { setIsChildLoading(true); try { const urlMain = myUrl + "/section2/endpoint"; const result = await ReactCall.post(urlMain); responseHandler(result); if (result.data) { const mySection2Response = result?.data?.result?.sections.find(item => item.id === 'section-2'); setMySection2Obj({ attr1: mySection2Response?.fields?.attr1, attr2: mySection2Response?.fields?.attr2 }); props.updateDateTime(`Refreshed at ${result?.data?.refreshDateTime}`); } } catch (err) { // 可添加错误处理逻辑 console.error('API调用失败:', err); } finally { setIsChildLoading(false); // 无论成功失败都结束加载状态 } } return <div>{/* 子组件渲染内容 */}</div>; };
备选方案:优化父组件刷新逻辑
如果一定要保留tab切换的逻辑,可修改刷新时的状态更新,跳过中间的卸载步骤:
// 刷新按钮点击时直接设置为选中当前tab,避免子组件卸载 setMyTabs({...defaultTabs, [selectedTabId]: true});
但这种方式只能在组件首次挂载时触发一次API,刷新时仍需结合上述方案的refreshKey来主动触发API调用。
额外注意事项
- 开发环境下React严格模式的重复
useEffect是调试行为,生产环境不会出现,无需刻意规避。 - 给API调用添加
try/catch,避免请求失败导致加载状态一直处于true。 - 确保所有
useEffect的依赖项完整,避免React的依赖警告。
内容的提问来源于stack exchange,提问作者copenndthagen
相关产品推荐
相关产品推荐

