React数组项目多Ref实现PopperJS Tooltip报错求助
解决React遍历项目数组时PopperJS Tooltip的Ref问题
我在网页中添加项目列表,希望用户悬停项目图片时,左侧显示包含项目详情的Tooltip,用PopperJS实现。单个项目用useRef能正常运行,但处理数组项目时,尝试给每个项目创建不同useRef时出现错误:Function components cannot have string refs,现有代码如下,求解决方法。
原项目列表组件代码
import React, { useState, useRef } from "react"; import Card from "./UI/Card"; import ToolTip from "./UI/Tooltip"; import ProjectData from "./data/Projects.json"; const Projects = (props) => { const MidpointRef = useRef(); const SpacesRef = useRef(); const MovieMinderRef = useRef(); const midpointTooltipRef = useRef(); const spacesTooltipRef = useRef(); const MovieMinderTooltipRef = useRef(); const [isOpen, setIsOpen] = useState(false); const toggleToolTip = () => { setIsOpen(!isOpen); }; return ProjectData.map((project) => { return ( <Card> <div key={project.id} className={`flex project-thumbnail ${project.Name}`} onMouseEnter={toggleToolTip} onMouseLeave={toggleToolTip} boxRef={`${project.Name}Ref`} > <button class="view-button" onMouseEnter={props.onMouseEnter} onMouseLeave={props.onMouseLeave} > Click to view </button> </div> <ToolTip ref={`${project.Name}TooltipRef`} toolTipState={isOpen} boxRef={`${project.Name}Ref`} /> </Card> ); }); };
注:onMouseEnter/onMouseLeave为缩小光标事件处理函数
原Tooltip组件代码
import classNames from "classnames"; import { useRef, forwardRef } from "react"; import { usePopper } from "react-popper"; const ToolTip = forwardRef((props, ref) => { const { attributes } = usePopper( props.boxRef.current, ref.current, {} ); return ( <div id="tooltip" role="tooltip" className={classNames("tooltip", { "tooltip-active": props.toolTipState, })} ref={ref} {...attributes.popper} > My tooltip </div> ); }); export default ToolTip;
解决方法及修改后的代码
核心问题是:字符串Ref已被React废弃,且单个状态会导致所有Tooltip同步显示/隐藏。以下是修正方案:
修改后的Projects组件
import React, { useState, useRef } from "react"; import Card from "./UI/Card"; import ToolTip from "./UI/Tooltip"; import ProjectData from "./data/Projects.json"; const Projects = (props) => { // 用对象统一存储所有项目的容器Ref和Tooltip Ref const projectRefs = useRef({}); const tooltipRefs = useRef({}); // 为每个项目单独维护Tooltip显示状态 const [tooltipStates, setTooltipStates] = useState({}); // 切换单个项目的Tooltip状态 const toggleToolTip = (projectId) => { setTooltipStates(prev => ({ ...prev, [projectId]: !prev[projectId] })); }; return ProjectData.map((project) => { return ( <Card key={project.id}> <div className={`flex project-thumbnail ${project.Name}`} onMouseEnter={() => toggleToolTip(project.id)} onMouseLeave={() => toggleToolTip(project.id)} // 通过ref回调存入对应项目的DOM元素 ref={el => projectRefs.current[project.id] = el} > <button className="view-button" onMouseEnter={props.onMouseEnter} onMouseLeave={props.onMouseLeave} > Click to view </button> </div> <ToolTip // 存入对应项目的Tooltip DOM元素 ref={el => tooltipRefs.current[project.id] = el} toolTipState={tooltipStates[project.id] || false} // 传递当前项目的容器Ref boxRef={projectRefs.current[project.id]} // 可传入项目详情用于渲染 projectDetails={project.Description} /> </Card> ); }); }; export default Projects;
修改后的Tooltip组件
import classNames from "classnames"; import { forwardRef, useEffect, useRef } from "react"; import { usePopper } from "react-popper"; const ToolTip = forwardRef((props, ref) => { const popperRef = useRef(null); // 同步forwardRef的引用 useEffect(() => { popperRef.current = ref.current; }, [ref]); // 确保容器Ref存在时再初始化Popper,避免空引用错误 const { attributes, styles } = usePopper( props.boxRef, popperRef.current, { placement: "left", // 设置Tooltip显示在左侧 modifiers: [ { name: "offset", options: { offset: [0, 10], // 调整偏移量避免紧贴元素 }, }, ], } ); return ( <div role="tooltip" className={classNames("tooltip", { "tooltip-active": props.toolTipState, })} ref={ref} style={styles.popper} {...attributes.popper} > {props.projectDetails || "项目详情"} </div> ); }); export default ToolTip;
关键修改点说明
- Ref管理优化:用
projectRefs和tooltipRefs两个对象Ref统一存储所有项目的DOM元素,通过ref回调函数绑定,彻底避免字符串Ref的错误。 - 状态独立控制:用
tooltipStates对象存储每个项目的Tooltip状态,确保只有当前悬停的项目Tooltip会触发显示/隐藏,不会影响其他项目。 - Popper配置完善:添加了左侧显示的placement配置,增加偏移量优化显示效果,同时增加空引用判断避免初始化错误。
- React规范修正:将button的
class改为React规范的className,给循环根元素Card添加了唯一key。
内容的提问来源于stack exchange,提问作者d709859
相关产品推荐
相关产品推荐

