React中使用Gridjs为TD元素添加addEventListener触发多次事件的问题排查
问题分析与解决方案
你遇到的事件重复触发问题,核心原因是**useEffect没有设置依赖数组,导致每次组件渲染时都会重复执行effect内容**——包括重复渲染Grid表格、重复添加点击事件监听器。每渲染一次就多绑定一个监听器,点击时自然会触发多次。
修复方案1:修正useEffect的依赖与清理逻辑
首先,给useEffect添加空依赖数组,确保它只在组件挂载时执行一次;同时添加清理函数,移除事件监听器并销毁Grid实例,避免内存泄漏:
import React, { useEffect, useRef } from "react"; import { Grid as Gridjs } from "gridjs"; function App() { const wrapperRef = useRef(null); const grid = new Gridjs({ columns: ["Name", "Email", "Phone Number"], data: [ ["John", "john@example.com", "(353) 01 222 3333"], ["Mark", "mark@gmail.com", "(01) 22 888 4444"] ] }); useEffect(() => { // 仅挂载时渲染一次表格 grid.render(wrapperRef.current); // 提取点击处理函数,方便后续移除监听 const handleCellClick = (ev) => { if (ev.target.tagName === "TD") { console.log(ev.target.dataset.columnId); } }; wrapperRef.current.addEventListener("click", handleCellClick); // 组件卸载时清理资源 return () => { wrapperRef.current.removeEventListener("click", handleCellClick); grid.destroy(); // 销毁Grid实例,释放内存 }; }, []); // 空依赖数组:仅在组件挂载/卸载时执行 return ( <div className="App"> <header className="App-header"></header> <div ref={wrapperRef} /> </div> ); } export default App;
修复方案2:使用Gridjs原生配置(更推荐)
其实Gridjs本身提供了单元格级别的事件配置,不需要手动通过addEventListener绑定,这种方式更符合Gridjs的设计逻辑,也能避免事件委托的潜在问题:
import React, { useEffect, useRef } from "react"; import { Grid as Gridjs, h } from "gridjs"; function App() { const wrapperRef = useRef(null); // 直接在columns配置中给单元格添加点击事件 const grid = new Gridjs({ columns: [ { name: "Name", cell: (value, row) => h('td', { onClick: () => { console.log(`点击了Name列,值:${value},行索引:${row.index}`); } }, value) }, { name: "Email", cell: (value, row) => h('td', { onClick: () => { console.log(`点击了Email列,值:${value},行索引:${row.index}`); } }, value) }, { name: "Phone Number", cell: (value, row) => h('td', { onClick: () => { console.log(`点击了Phone Number列,值:${value},行索引:${row.index}`); } }, value) } ], data: [ ["John", "john@example.com", "(353) 01 222 3333"], ["Mark", "mark@gmail.com", "(01) 22 888 4444"] ] }); useEffect(() => { grid.render(wrapperRef.current); return () => { grid.destroy(); }; }, []); return ( <div className="App"> <header className="App-header"></header> <div ref={wrapperRef} /> </div> ); } export default App;
如果需要给所有列统一添加点击逻辑,可以封装一个通用的cell函数,避免重复代码:
const createClickableCell = (columnName) => (value, row) => h('td', { onClick: () => { console.log(`点击了${columnName}列,值:${value},行索引:${row.index}`); } }, value); // 然后在columns中使用: columns: [ { name: "Name", cell: createClickableCell("Name") }, { name: "Email", cell: createClickableCell("Email") }, { name: "Phone Number", cell: createClickableCell("Phone Number") }, ]
内容的提问来源于stack exchange,提问作者mhanuel
相关产品推荐
相关产品推荐

