You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 19:32:35