React子组件API调用两次排查(Strict Mode已关闭)
子组件重复渲染与API调用两次的原因及修复方案
核心原因
1. 父组件UserDetails的两次渲染触发子组件重复渲染
useParams()在路由初始化过程中可能存在**先返回空参数对象,再加载到正确userId**的情况,导致父组件先后渲染两次:
- 第一次渲染:
params.userId为undefined,子组件接收undefined作为userId - 第二次渲染:
params.userId获取到有效值223,子组件接收新的userId
这两次父组件渲染会直接触发子组件的两次渲染,进而导致子组件的useEffect执行两次。
2. 子组件useEffect依赖数组缺失关键参数
子组件的useEffect中使用了props.userId,但未将其加入依赖数组,这会引发两个问题:
- 当
userId更新时,useEffect不会主动触发,导致请求可能使用旧的userId值(闭包陷阱) - 若父组件因
userId变化重新渲染子组件,useEffect可能因其他依赖的隐式变化(比如对象引用)重复执行
修复方案
步骤1:修复父组件的参数处理
给params.userId添加默认值,仅在参数有效时渲染子组件,减少不必要的渲染:
export const UserDetails = () => { const params = useParams(); // 解构时赋予默认值,避免传递undefined const userId = params?.userId || ''; useEffect(() => { if(userId){ getCustomerDetails(userId) } }, [userId]); return ( <div> {/* 只在userId有效时渲染子组件 */} {userId && ( <> <UserSpecificData1 userId={userId}/> <UserSpecificData2 userId={userId}/> </> )} </div> ); };
步骤2:补全子组件useEffect的依赖数组
将props.userId加入useEffect的依赖数组,确保userId变化时能重新触发API请求,同时避免闭包问题:
// 以UserSpecificData1为例,UserSpecificData2做相同修改 const UserSpecificData1 = props => { const [currentPage, setCurrentPage] = useState(0); const [filteredBy, setFilteredBy] = useState({}); const [sortBy, setSortBy] = useState('ASC'); useEffect(() => { // 仅当userId有效时发起请求 if(!props.userId) return; getSpecificDataOne({ id: props.userId, filteredBy: filteredBy, page: currentPage, size: 10, sortBy: sortBy, }) }, [sortBy, currentPage, filteredBy, props.userId]); // 加入props.userId return <div></div> };
步骤3:优化引用类型依赖(可选)
由于filteredBy是对象类型,若后续需要修改它,建议使用useMemo缓存对象,避免因引用变化触发不必要的useEffect执行:
// 使用useMemo缓存filteredBy,确保引用稳定 const filteredBy = useMemo(() => ({}), []);
内容的提问来源于stack exchange,提问作者Mihika Moon
相关产品推荐
相关产品推荐

