MUI DataGrid新增行时SelectField值未重置的问题求助
MUI DataGrid 添加新行时SelectField保留上一行值的问题
我在使用MUI的DataGrid组件,表格包含id(数字)、processPath(下拉选择框)、associateLogin(文本框)、action(按钮)四列,已经实现了添加/删除行的功能。但现在遇到一个问题:processPath列的下拉框虽然能正常更新值,但每次添加新行时,这个字段会自动带上上一行选中的值。我初步判断问题出在复用的自定义函数组件上,但不知道怎么把新行的这个字段状态重置为空。
原始代码
import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent, Box, Button, Stack, } from "@mui/material"; import { DataGrid, } from "@mui/x-data-grid"; import React, { FunctionComponent, useState } from "react"; const processPaths: string[] = ["Test1", "Test2"]; export const AccomodationsTable: FunctionComponent = () => { const [idCounter, setIdCounter] = useState(0); const [rowCounter, setRowCounter] = useState(0); const [formData, setFormData] = useState([ { id: idCounter, processPath: "", associateLogin: "", action: "", }, ]); const ProcessPathSelectField: FunctionComponent = () => { function handleChange(event: SelectChangeEvent) { const { name, value } = event.target; setFormData((prevFormData) => ({ ...prevFormData, [name]: value, })); } return ( <Box> <FormControl fullWidth> <InputLabel id={`process-path-select-${idCounter}`}> Please Select </InputLabel> <Select labelId="process-path-select" id={`process-path-select-${idCounter}`} value={formData[0].processPath} name="processPath" label={`process-path-select-${idCounter}`} onChange={handleChange} sx={{ minWidth: "240px" }} > {processPaths.sort().map((processPath) => ( <MenuItem value={processPath}>{processPath}</MenuItem> ))} </Select> </FormControl> </Box> ); }; const createNewRow = () => { setIdCounter((prevIdCounter) => (prevIdCounter + 1)); return { id: idCounter, processPath: "", associateLogin: "", action: "", }; }; const [rows, setRows] = useState(() => [createNewRow()]); const handleAddRow = () => { setRows((prevRows) => [...prevRows, createNewRow()]); }; const handleRemoveRow = () => { setRows(rows.slice(0, idCounter - 1)); setIdCounter((prevIdCounter) => (prevIdCounter - 1)); }; const columns = [ { field: "id" }, { field: "Process Path", width: 270, renderCell: () => <ProcessPathSelectField />, }, { field: "Associate Login", width: 270 }, { field: "Action", width: 270 }, ]; return ( <> <Box sx={{ width: "100%" }}> <Stack direction="row" spacing={1} sx={{ mb: 1 }}> <Button size="small" onClick={handleRemoveRow}> Remove a row </Button> <Button size="small" onClick={handleAddRow}> Add a row </Button> </Stack> <Box sx={{ height: 400, mt: 1 }}> <DataGrid rows={rows} columns={columns} columnVisibilityModel={{ id: false, }} /> </Box> </Box> </> ); };
问题根源
- 状态冗余且脱节:同时维护
formData和rows两个状态,下拉框绑定的是formData[0]的固定值,所有行共享同一个下拉状态。 - 自定义组件未关联行数据:
ProcessPathSelectField没有接收当前行的id和数据,无法针对单行做独立更新。 - id生成逻辑异步问题:调用
setIdCounter后立即返回旧的idCounter值,导致行id重复或不准确。
修复后的完整代码
import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent, Box, Button, Stack, } from "@mui/material"; import { DataGrid, GridRenderCellParams } from "@mui/x-data-grid"; import React, { useState } from "react"; const processPaths: string[] = ["Test1", "Test2"]; export const AccomodationsTable = () => { // 统一用rows维护所有行数据,删除冗余状态 const [rows, setRows] = useState([{ id: 0, processPath: "", associateLogin: "", action: "", }]); // 针对单行更新processPath值 const handleProcessPathChange = (id: number, value: string) => { setRows(prevRows => prevRows.map(row => row.id === id ? {...row, processPath: value} : row ) ); }; // 生成唯一id的新行,确保初始processPath为空 const createNewRow = () => { const maxId = rows.length > 0 ? Math.max(...rows.map(r => r.id)) : 0; return { id: maxId + 1, processPath: "", associateLogin: "", action: "", }; }; const handleAddRow = () => { setRows(prevRows => [...prevRows, createNewRow()]); }; const handleRemoveRow = () => { if (rows.length <= 1) return; // 至少保留一行 setRows(prevRows => prevRows.slice(0, -1)); }; // 下拉框组件接收当前行参数,绑定对应行的processPath const ProcessPathSelectField = (params: GridRenderCellParams) => { const { row } = params; return ( <Box> <FormControl fullWidth> <InputLabel id={`process-path-select-${row.id}`}> Please Select </InputLabel> <Select labelId={`process-path-select-${row.id}`} id={`process-path-select-${row.id}`} value={row.processPath} label="Please Select" onChange={(e) => handleProcessPathChange(row.id, e.target.value as string)} sx={{ minWidth: "240px" }} > {processPaths.sort().map((processPath) => ( <MenuItem key={processPath} value={processPath}>{processPath}</MenuItem> ))} </Select> </FormControl> </Box> ); }; const columns = [ { field: "id", hide: true }, { field: "Process Path", width: 270, renderCell: (params) => <ProcessPathSelectField {...params} />, }, { field: "Associate Login", width: 270, // 补充文本框编辑逻辑 renderCell: (params) => ( <input type="text" value={params.row.associateLogin} onChange={(e) => { setRows(prevRows => prevRows.map(row => row.id === params.row.id ? {...row, associateLogin: e.target.value} : row )); }} style={{ width: '100%', padding: '8px', boxSizing: 'border-box' }} /> ) }, { field: "Action", width: 270, // 补充单行删除逻辑 renderCell: (params) => ( <Button size="small" onClick={() => { setRows(prevRows => prevRows.filter(row => row.id !== params.row.id)); }} > 删除此行 </Button> ) }, ]; return ( <> <Box sx={{ width: "100%" }}> <Stack direction="row" spacing={1} sx={{ mb: 1 }}> <Button size="small" onClick={handleRemoveRow} disabled={rows.length <=1}> Remove a row </Button> <Button size="small" onClick={handleAddRow}> Add a row </Button> </Stack> <Box sx={{ height: 400, mt: 1 }}> <DataGrid rows={rows} columns={columns} /> </Box> </Box> </> ); };
关键修复点
- 合并状态管理:删除
formData和idCounter,所有行数据统一存在rows中,确保状态一致。 - 组件关联行数据:让
ProcessPathSelectField接收DataGrid的行参数,每个下拉框绑定对应行的processPath值,实现独立状态。 - 精准更新逻辑:通过行id定位,只修改当前行的
processPath,避免影响其他行。 - 修复id生成:基于现有行的最大id生成新id,解决异步更新导致的id错误问题。
- 完善交互:补充文本框编辑、单行删除功能,添加删除行的最小数量限制。
内容的提问来源于stack exchange,提问作者PandasM
相关产品推荐
相关产品推荐

