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
相关产品推荐
相关产品推荐

