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

React中MUI X Data Grid图标按钮onClick单次点击不触发问题求助

问题描述

在React.js中使用MUI X Data Grid配置包含操作列的表格,图标显示正常,但所有图标按钮的onClick事件触发不稳定:有时单次点击生效,有时需要点击两次。

操作列代码

{
      flex: 0.1,
      minWidth: 150,
      sortable: false,
      field: "actions",
      headerName: "",
      renderCell: ({ row }) => (
        <Box>
          <IconButton color="primary" aria-label="View Details" tabIndex={0} title="View Details">
            <Icon icon='tabler:eye' onClick={ () => {navigate(`/employees/detail/${row.id}`);}} />
          </IconButton>
          <IconButton color="main" aria-label="Edit" tabIndex={0} onClick={() =>{handleEditEmployee(row.id)}} title="Edit">
            <Icon icon='tabler:pencil' />
          </IconButton>
          <IconButton color="error" aria-label="Delete" tabIndex={0} onClick={() =>{showConfirmation(row.id)}} title="Delete">
            <Icon icon="tabler:trash" />
          </IconButton>
        </Box>        
      ),
    }

MUI依赖

  • "@mui/icons-material": "^5.16.0"
  • "@mui/lab": "^5.0.0-alpha.173"
  • "@mui/material": "^5.16.0"
  • "@mui/x-data-grid": "^7.11.0"
  • "@mui/x-date-pickers": "^7.12.1"
  • "@mui/x-tree-view": "^7.20.0"

DataGrid配置

<DataGrid
          editMode="row"
          autoHeight
          pagination
          rows={filteredRows}
          rowHeight={62}
          columns={columns}
          checkboxSelection
          pageSizeOptions={[5, 10]}
          disableRowSelectionOnClick
          paginationModel={paginationModel}
          onPaginationModelChange={setPaginationModel}
          onRowSelectionModelChange={(newSelectionModel) => {
            handleGridSelection(newSelectionModel);
          }}
          hideFooterSelectedRowCount
          getRowId={(row) => row.id}                                 
          processRowUpdate={(updatedRow, originalRow) => handleRowDataChange(updatedRow, originalRow)}
          onProcessRowUpdateError={handleProcessRowUpdateError}     
          sx={{
            '& .MuiDataGrid-columnHeaderTitle': {
              textTransform: 'none', // This will disable the capitalization
            },
          }} 
          slots={{
            noRowsOverlay: NoRowsOverlay,
          }} 
          initialState={{
            sorting: {
              sortModel: [{ field: 'Fullname', sort: 'asc' }],
            },
          }}   
        />

处理器代码

const handleEditEmployee = (id) => {
    resetEmployee();
    let emp = getEmployeeById(id);    
    setEmployee(emp);
    setModalData({title:'Edit Employee', btnTxt:'Save Changes', successTitle:'Updated!', successTxt:'Employee information successfully updated.'})
    setOpenNewCustomerDlg(true);
  }
const handleDeleteEmployee = async (empID, Name) =>{    
      try {
        const result = await DeleteEmployee(empID,navigate);
        if (result && result.form) {
          console.error(result.form);
          console.log(result.form);
        } else {
          console.log('Employee deleted successfully!'); 
          Swal.fire(
            {
              title: 'Deleted!',
              text: `${Name} has been deleted.`,
              icon: 'success',
              confirmButtonColor: '#23a26d', 
            }         
          );
          fetchData();       
        }
      } catch (error) {
        console.error('Error deleting employee:', error);      
      }
  }

const showConfirmation = (id, Name) => {
MySwal.fire({
  title: 'Are you sure?',
  text: "You won't be able to revert this!",
  icon: 'warning',
  showCancelButton: true,
  confirmButtonColor: '#23A26D',
  cancelButtonColor: '#d33',
  confirmButtonText: 'Yes, delete it!',
  cancelButtonText: 'Cancel',
}).then((result) => {
  if (result.isConfirmed) {
    // Perform delete action here
    handleDeleteEmployee(id,Name);        
  }
});
}
解决方案

1. 将点击事件绑定到IconButton组件

当前代码将onClick绑定到内部的Icon元素,而IconButton才是负责处理点击交互的容器组件,这可能导致事件被拦截或冒泡异常。修改操作列代码,把onClick移到IconButton上,并移除手动设置的tabIndex(IconButton默认已配置合适的焦点行为):

{
  flex: 0.1,
  minWidth: 150,
  sortable: false,
  field: "actions",
  headerName: "",
  renderCell: ({ row }) => (
    <Box>
      <IconButton 
        color="primary" 
        aria-label="View Details" 
        title="View Details"
        onClick={() => navigate(`/employees/detail/${row.id}`)}
      >
        <Icon icon='tabler:eye' />
      </IconButton>
      <IconButton 
        color="main" 
        aria-label="Edit" 
        title="Edit"
        onClick={() => handleEditEmployee(row.id)}
      >
        <Icon icon='tabler:pencil' />
      </IconButton>
      <IconButton 
        color="error" 
        aria-label="Delete" 
        title="Delete"
        onClick={() => showConfirmation(row.id)}
      >
        <Icon icon="tabler:trash" />
      </IconButton>
    </Box>        
  ),
}

2. 排除操作列的行编辑干扰

由于开启了editMode="row",第一次点击行内元素可能触发行进入编辑状态,导致按钮点击事件被延迟。在操作列定义中添加editable: false,明确该列不可编辑:

{
  flex: 0.1,
  minWidth: 150,
  sortable: false,
  field: "actions",
  headerName: "",
  editable: false, // 新增这一行
  renderCell: ({ row }) => (
    // 原有渲染代码
  ),
}

3. 用useCallback稳定处理器函数引用

如果处理器函数每次渲染都重新创建,会导致renderCell生成新的组件实例,间接影响事件绑定稳定性。使用useCallback缓存函数:

import { useCallback } from 'react';

const handleEditEmployee = useCallback((id) => {
  resetEmployee();
  const emp = getEmployeeById(id);    
  setEmployee(emp);
  setModalData({
    title:'Edit Employee', 
    btnTxt:'Save Changes', 
    successTitle:'Updated!', 
    successTxt:'Employee information successfully updated.'
  });
  setOpenNewCustomerDlg(true);
}, [resetEmployee, getEmployeeById, setEmployee, setModalData, setOpenNewCustomerDlg]);

const showConfirmation = useCallback((id) => {
  MySwal.fire({
    title: 'Are you sure?',
    text: "You won't be able to revert this!",
    icon: 'warning',
    showCancelButton: true,
    confirmButtonColor: '#23A26D',
    cancelButtonColor: '#d33',
    confirmButtonText: 'Yes, delete it!',
    cancelButtonText: 'Cancel',
  }).then((result) => {
    if (result.isConfirmed) {
      handleDeleteEmployee(id);        
    }
  });
}, [handleDeleteEmployee]);

4. 阻止事件冒泡到DataGrid行

如果点击按钮时仍触发行的编辑或选择逻辑,在onClick中添加事件阻止冒泡:

<IconButton 
  color="primary" 
  aria-label="View Details" 
  title="View Details"
  onClick={(e) => {
    e.stopPropagation(); // 阻止事件冒泡到父元素
    navigate(`/employees/detail/${row.id}`);
  }}
>
  <Icon icon='tabler:eye' />
</IconButton>

内容的提问来源于stack exchange,提问作者Umair Farooq

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 23:32:03