React多元素点击弹窗展示问题:useRef传引用报错求解决方案
问题分析与解决方案
首先明确:不能直接渲染useRef存储的真实DOM节点,React的渲染系统只能处理React元素(JSX),而expendRef.current是浏览器生成的真实HTMLDivElement对象,直接放进JSX会触发类型错误。
先修正你代码里的基础错误
你的代码存在几个语法/写法问题,先修正这些才能正常运行:
- 缺少
useState和useRef的解构导入 Popup组件写法错误,函数组件无需render方法,直接返回JSX即可onClick事件的箭头函数括号未闭合expendRef.current?.parentNode是DOM节点,不能直接作为React子元素渲染
修正后的基础代码(仍未解决核心需求):
import React, { useState, useRef } from "react"; const Summary = () => { const [expand, setExpand] = useState(false); const expendRef = useRef<HTMLDivElement>(null); return ( <div> {expand && <Popup show={expand} />} <div ref={expendRef} className='summary-table col-md-6' onClick={() => setExpand(!expand)} > Reference element </div> </div> ); }; const Popup = (props) => { return ( <> <h1>Popup Element</h1> <div>{props.children}</div> </> ); }; export default Summary;
可行实现方案
方案1:存储元素对应的业务数据(推荐)
不要存储DOM节点,而是把每个元素的详情数据存起来,点击时将对应数据传给弹窗渲染。这是多元素场景(如联系人网格)最常用的方案:
import React, { useState } from "react"; // 模拟联系人数据 const contacts = [ { id: 1, name: "张三", phone: "138xxxx1234", email: "zhangsan@example.com" }, { id: 2, name: "李四", phone: "139xxxx5678", email: "lisi@example.com" }, ]; const Summary = () => { const [selectedContact, setSelectedContact] = useState(null); return ( <div> {/* 弹窗:根据选中的联系人渲染详情 */} {selectedContact && ( <Popup onClose={() => setSelectedContact(null)}> <div> <h2>{selectedContact.name}</h2> <p>电话:{selectedContact.phone}</p> <p>邮箱:{selectedContact.email}</p> </div> </Popup> )} {/* 联系人网格 */} <div className="contacts-grid"> {contacts.map(contact => ( <div key={contact.id} className='summary-table col-md-6' onClick={() => setSelectedContact(contact)} > {contact.name} </div> ))} </div> </div> ); }; const Popup = ({ onClose, children }) => { return ( <div className="popup-overlay" onClick={onClose}> <div className="popup-content" onClick={(e) => e.stopPropagation()}> <button onClick={onClose}>关闭</button> {children} </div> </div> ); }; export default Summary;
方案2:抽离可复用的内容组件
如果需要复用原元素的UI样式,可以把元素的内容抽成独立组件,点击时在弹窗里重新渲染该组件:
import React, { useState } from "react"; // 抽离可复用的内容组件 const ContactCard = ({ contact }) => ( <div className='summary-card'> <h3>{contact.name}</h3> <p>电话:{contact.phone}</p> </div> ); const Summary = () => { const [selectedContact, setSelectedContact] = useState(null); return ( <div> {selectedContact && ( <Popup onClose={() => setSelectedContact(null)}> <ContactCard contact={selectedContact} /> </Popup> )} <div className="contacts-grid"> {contacts.map(contact => ( <div key={contact.id} className='summary-table col-md-6' onClick={() => setSelectedContact(contact)} > <ContactCard contact={contact} /> </div> ))} </div> </div> ); }; // Popup组件同方案1 const Popup = ({ onClose, children }) => { return ( <div className="popup-overlay" onClick={onClose}> <div className="popup-content" onClick={(e) => e.stopPropagation()}> <button onClick={onClose}>关闭</button> {children} </div> </div> ); }; export default Summary;
为什么不能直接渲染DOM节点?
React基于虚拟DOM机制工作,要求渲染的是React元素(JSX编译后的对象)。真实DOM节点是浏览器创建的原生对象,无法被React纳入虚拟DOM的更新流程,直接放入JSX会触发类型不匹配的错误。
内容的提问来源于stack exchange,提问作者Anurag Sharma
相关产品推荐
相关产品推荐

