React DataGrid增改记录问题:getActions未获取id致按钮不切换
React DataGrid中getActions无法获取id,编辑按钮无法切换状态问题
我跟着React教程实现基于DataGrid的CRUD数据操作功能,但遇到了问题:代码中的getActions方法无法正确获取行id对应的编辑状态,导致isInEditMode始终为false,点击编辑按钮后无法切换为保存和取消按钮。
相关代码
AdminView组件代码
import React, { useEffect, useState } from 'react'; import axios from 'axios'; import { Box, Typography } from '@mui/material'; import Button from '@mui/material/Button'; import AddIcon from '@mui/icons-material/Add'; import EditIcon from '@mui/icons-material/Edit'; import DeleteIcon from '@mui/icons-material/DeleteOutlined'; import SaveIcon from '@mui/icons-material/Save'; import CancelIcon from '@mui/icons-material/Close'; import EditToolbar from './EditToolbar'; import { GridRowModes, DataGrid, GridToolbarContainer, GridActionsCellItem, GridRowEditStopReasons, } from '@mui/x-data-grid'; const AdminView = ()=> { const [rows, setRows] = React.useState([]); const [rowModesModel, setRowModesModel] = React.useState({}); useEffect(() => { axios.get('http://localhost:3001/users') .then(response => { setRows(response.data); }) .catch(error => { console.error('Error fetching user data:', error); }); }, []); const [columns, setColumns] = useState([ { field: "id"}, { field: "staff_number", editable: true }, { field: "name", editable: true }, { field: "surname", editable: true }, { field: "email", editable: true }, { field: "password", editable: true }, { field: "access_level", editable: true }, { field: 'actions', type: 'actions', headerName: 'Actions', width: 100, cellClassName: 'actions', getActions: ({ id }) => { console.log('get action:', rowModesModel[id]); const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; if (isInEditMode) { return [ <GridActionsCellItem icon={<SaveIcon />} label="Save" sx={{ color: 'primary.main' }} onClick={handleSaveClick(id)} />, <GridActionsCellItem icon={<CancelIcon />} label="Cancel" className="textPrimary" onClick={handleCancelClick(id)} color="inherit" />, ]; } return [ <GridActionsCellItem icon={<EditIcon />} label="Edit" className="textPrimary" onClick={handleEditClick(id)} color="inherit" />, <GridActionsCellItem icon={<DeleteIcon />} label="Delete" onClick={handleDeleteClick(id)} color="inherit" />, ]; }, } ]); const handleEditClick = (id) => () => { setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } }); }; const handleSaveClick = (id) => () => { console.log('handleSaveClick:', id); setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } }); }; const handleDeleteClick = (id) => () => { setRows(rows.filter((row) => row.id !== id)); }; const handleCancelClick = (id) => () => { setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View, ignoreModifications: true }, }); const editedRow = rows.find((row) => row.id === id); if (editedRow.isNew) { setRows(rows.filter((row) => row.id !== id)); } }; const handleModeRowsChange = (newRowModesModel ) => { console.log('newRowModesModel change to:', newRowModesModel); setRowModesModel(newRowModesModel); } const handleRowEditStop = (params, event) => { console.log('handleRowEditStop params:', params); if (params.reason === GridRowEditStopReasons.rowFocusOut) { event.defaultMuiPrevented = true; } }; const processRowUpdate = (newRow) => { console.log('processRowUpdate newRow returned:', newRow); const updatedRow = { ...newRow, isNew: false }; setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row))); console.log('updatedRow returned:', updatedRow); return updatedRow; }; return ( <div> <DataGrid rows={rows} columns={columns} editMode="row" checkboxSelection rowModesModel={rowModesModel} onRowModesModelChange={handleModeRowsChange} onRowEditStop={handleRowEditStop} processRowUpdate={processRowUpdate} slots={{ toolbar: EditToolbar }} slotProps={{ toolbar: { setRows, setRowModesModel } }} /> </div> ); }; export default AdminView;
EditToolbar组件代码
import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import AddIcon from '@mui/icons-material/Add'; import { GridToolbarContainer } from '@mui/x-data-grid'; import { randomId } from '@mui/x-data-grid-generator'; import { GridRowModes } from '@mui/x-data-grid'; function EditToolbar(props) { const { setRows, setRowModesModel } = props; const handleClick = () => { const id = randomId(); setRows((oldRows) => [ ...oldRows, { id, name: '', surname: '', email: '', password: '', access_level: '', staff_number: '', isNew: true } ]); setRowModesModel((oldModel) => ({ ...oldModel, [id]: { mode: GridRowModes.Edit, fieldToFocus: 'staff_number' } })); }; return ( <GridToolbarContainer> <Button color="primary" startIcon={<AddIcon />} onClick={handleClick}> Add record </Button> </GridToolbarContainer> ); } export default EditToolbar;
问题原因及修复方案
1. 核心问题:Columns使用useState导致状态不更新
你把columns放在useState中,但没有添加任何依赖项。这意味着组件渲染时,columns只会初始化一次,后续rowModesModel变化时,getActions方法中的rowModesModel始终是初始的空对象,无法正确判断行的编辑状态。
修复:
把columns从useState改成普通常量,去掉状态包裹:
// 替换原来的const [columns, setColumns] = useState([...]) const columns = [ { field: "id"}, { field: "staff_number", editable: true }, { field: "name", editable: true }, { field: "surname", editable: true }, { field: "email", editable: true }, { field: "password", editable: true }, { field: "access_level", editable: true }, { field: 'actions', type: 'actions', headerName: 'Actions', width: 100, cellClassName: 'actions', getActions: ({ id }) => { console.log('get action:', rowModesModel[id]); const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; if (isInEditMode) { return [ <GridActionsCellItem icon={<SaveIcon />} label="Save" sx={{ color: 'primary.main' }} onClick={handleSaveClick(id)} />, <GridActionsCellItem icon={<CancelIcon />} label="Cancel" className="textPrimary" onClick={handleCancelClick(id)} color="inherit" />, ]; } return [ <GridActionsCellItem icon={<EditIcon />} label="Edit" className="textPrimary" onClick={handleEditClick(id)} color="inherit" />, <GridActionsCellItem icon={<DeleteIcon />} label="Delete" onClick={handleDeleteClick(id)} color="inherit" />, ]; }, } ];
2. 可选优化:确保ID类型一致
如果后端返回的id是数字类型,而新增行的id是字符串,可能导致匹配失败。可以在获取数据时统一转换为字符串:
useEffect(() => { axios.get('http://localhost:3001/users') .then(response => { // 将id转换为字符串,保持类型一致 const formattedRows = response.data.map(row => ({ ...row, id: String(row.id) })); setRows(formattedRows); }) .catch(error => { console.error('Error fetching user data:', error); }); }, []);
3. 可选优化:使用函数式更新避免闭包问题
在更新状态时,使用函数式更新获取最新的状态值,避免闭包导致的旧状态问题:
const handleEditClick = (id) => () => { setRowModesModel(prevModel => ({ ...prevModel, [id]: { mode: GridRowModes.Edit } })); }; const handleSaveClick = (id) => () => { setRowModesModel(prevModel => ({ ...prevModel, [id]: { mode: GridRowModes.View } })); };
内容的提问来源于stack exchange,提问作者Damiano Miazzi
相关产品推荐
相关产品推荐

