Fixed Data Table中react-tippy tooltip内容事件无法触发的问题
解决react-tippy tooltip中事件无法触发的问题
我遇到过类似的情况,核心问题在于react-tippy的html选项传入的JSX并没有被React的事件系统管理——tippy内部会把这段JSX转换成静态DOM节点直接插入页面,而这些节点脱离了React的渲染流程,导致你绑定的onClick、onMouseEnter等React事件完全不会被触发。
下面给你两种可行的解决方案:
方案一:使用原生JavaScript事件绑定
在tooltip显示时,手动给内容元素添加原生事件监听,绕开React事件系统的限制:
import React from 'react'; import { Cell } from 'fixed-data-table-2'; import { Tooltip } from 'react-tippy'; import 'actionCell.scss'; const { StyleSheet, css } = require('aphrodite'); export default class ActionCell extends React.PureComponent { // 绑定原生事件的回调 handleItemClick = (e) => { console.log('list item click:', e.target.textContent); // 这里可以根据点击的选项执行对应逻辑 }; handleItemMouseEnter = (e) => { console.log('list item mouse enter:', e.target.textContent); }; handleItemMouseLeave = (e) => { console.log('list item mouse leave:', e.target.textContent); }; // tooltip显示时绑定事件 handleTooltipShow = (instance) => { const itemNodes = instance.popper.querySelectorAll('.actions-list__item'); itemNodes.forEach(item => { // 先移除旧监听,避免重复绑定 item.removeEventListener('click', this.handleItemClick); item.removeEventListener('mouseenter', this.handleItemMouseEnter); item.removeEventListener('mouseleave', this.handleItemMouseLeave); // 添加原生事件监听 item.addEventListener('click', this.handleItemClick); item.addEventListener('mouseenter', this.handleItemMouseEnter); item.addEventListener('mouseleave', this.handleItemMouseLeave); }); }; render() { const { ...props } = this.props; return ( <Cell {...props} className={css(styles.actionCell)} onMouseEnter={() => console.log('cell mouse enter')} onMouseLeave={() => console.log('cell mouse leave')} > <Tooltip title="action" position="bottom" trigger="click" animation="fade" theme="light" arrow html={ <ul className="actions-list"> <li className="actions-list__item text-left">Close</li> <li className="actions-list__item text-left">Details</li> </ul> } onShow={this.handleTooltipShow} > <span>操作</span> </Tooltip> </Cell> ); } }
方案二:使用React Portal渲染tooltip内容
这种方式可以让tooltip内容完全处于React的管理之下,事件能正常触发,更符合React的开发习惯:
import React from 'react'; import { Cell } from 'fixed-data-table-2'; import { Tooltip } from 'react-tippy'; import ReactDOM from 'react-dom'; import 'actionCell.scss'; const { StyleSheet, css } = require('aphrodite'); // 单独抽离tooltip内容组件 const TooltipContent = () => { return ( <ul className="actions-list"> <li className="actions-list__item text-left" onClick={() => console.log('list item click: Close')} onMouseEnter={() => console.log('list item mouse enter: Close')}> Close </li> <li className="actions-list__item text-left" onClick={() => console.log('list item click: Details')} onMouseEnter={() => console.log('list item mouse enter: Details')}> Details </li> </ul> ); }; export default class ActionCell extends React.PureComponent { portalContainer = null; handleTooltipShow = (instance) => { // 获取tippy的内容容器 const contentEl = instance.popper.querySelector('.tippy-content'); if (contentEl) { contentEl.innerHTML = ''; this.portalContainer = contentEl; // 使用Portal渲染React组件 ReactDOM.render(<TooltipContent />, contentEl); } }; handleTooltipHide = () => { // 隐藏时卸载组件,避免内存泄漏 if (this.portalContainer) { ReactDOM.unmountComponentAtNode(this.portalContainer); this.portalContainer = null; } }; render() { const { ...props } = this.props; return ( <Cell {...props} className={css(styles.actionCell)} onMouseEnter={() => console.log('cell mouse enter')} onMouseLeave={() => console.log('cell mouse leave')} > <Tooltip title="action" position="bottom" trigger="click" animation="fade" theme="light" arrow html={<div></div>} {/* 先传入空容器占位 */} onShow={this.handleTooltipShow} onHide={this.handleTooltipHide} > <span>操作</span> </Tooltip> </Cell> ); } }
两种方案都能解决你的问题,方案二更贴近React的组件化思想,推荐使用。另外建议检查一下react-tippy的版本,确保依赖是最新的,避免版本兼容问题。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

