如何避免React Tooltip组件重复初始化以防止重复API请求
问题分析
你的核心问题是每次悬停时Tooltip和内部的UserInfoFetchData组件都会被销毁重建,原因在于OverlayTrigger的overlay属性直接传入了renderTooltip(gamer.uuid)的执行结果——每次父组件渲染时,这个函数都会被调用,生成全新的Tooltip和UserInfoFetchData实例,导致组件反复初始化、触发API请求。
解决方案
1. 阻止组件重复销毁重建:改用函数式overlay
OverlayTrigger的overlay支持传入一个函数,而不是直接传入组件元素。这种方式下,React会复用组件实例,而不是每次都创建新的。
修改调用代码:
// 保持renderTooltip函数,接收uuid和OverlayTrigger传递的props const renderTooltip = (uuid, triggerProps) => ( <Tooltip {...triggerProps}> {/* 传递OverlayTrigger的props给Tooltip */} <UserInfoFetchData uuid={uuid} /> </Tooltip> ); // 修改OverlayTrigger的overlay为函数形式 <OverlayTrigger placement="top" delay={{ show: 250, hide: 400 }} overlay={(props) => renderTooltip(gamer.uuid, props)} > <a href={`/user/${gamer.uuid}`}>{gamer.username}</a> </OverlayTrigger>
这样修改后,Tooltip和UserInfoFetchData组件不会在每次悬停时被销毁重建,只会在uuid变化时重新初始化。
2. 正确缓存API数据:内存级缓存替代localStorage
localStorage属于持久化存储,适合长期保存数据,但对于这种临时的用户资料,更适合用内存缓存,可以选择以下两种方式:
方式一:父组件维护缓存(推荐)
在渲染OverlayTrigger的父组件中,用useState或useRef维护一个缓存对象,存储已获取的用户数据,避免重复请求:
import { useState, useEffect } from 'react'; // 父组件中 const [userCache, setUserCache] = useState({}); // 改写为函数组件的UserInfoFetchData const UserInfoFetchData = ({ uuid }) => { const [userData, setUserData] = useState(userCache[uuid] || {}); useEffect(() => { if (userCache[uuid]) return; // 已有缓存,直接返回 const controller = new AbortController(); new Backend() .getUser({ uuid }, { signal: controller.signal }) .then((response) => { setUserData(response.data); setUserCache(prev => ({ ...prev, [uuid]: response.data })); }); return () => controller.abort(); }, [uuid, userCache]); // 渲染逻辑和原类组件一致,这里省略 };
方式二:组件内部用静态属性缓存
如果不想在父组件维护缓存,可以在UserInfoFetchData类组件中用静态属性存储全局缓存:
export default class UserInfoFetchData extends React.Component { static userCache = {}; // 静态属性,全局共享缓存 constructor(props) { super(props); this.state = UserInfoFetchData.userCache[this.props.uuid] || {}; } componentDidMount() { const { uuid } = this.props; if (UserInfoFetchData.userCache[uuid]) return; this.controller = new AbortController(); new Backend() .getUser({ uuid }, { signal: this.controller.signal }) .then((response) => { UserInfoFetchData.userCache[uuid] = response.data; this.setState(response.data); this.controller = null; }); } // 其他生命周期和渲染逻辑不变 }
额外优化:防抖请求
如果用户快速多次悬停,即使有缓存,也可能触发重复请求,可以在请求逻辑中加入防抖,或者在组件挂载时检查是否已有正在进行的请求。
内容的提问来源于stack exchange,提问作者Mark Cockram
相关产品推荐
相关产品推荐

