How can I change the MenuItems
of one Select
when another Select
component changes using DataGrid
? I need to be able to pass the state of one Select
component to the other, but I'm not sure how when using renderCell
.
For example, let's say I have the following object:
const data = {
"/path/to/file1.csv": {
parameters: ["Parameter 1", "Parameter 2", "Parameter 3"],
},
"/path/to/file2.csv": {
parameters: ["Parameter 2", "Parameter 3", "Parameter 4"],
},
"/path/to/file3.csv": {
parameters: ["Parameter 5", "Parameter 6", "Parameter 7"],
},
};
In my DataGrid
table, every time I add a new row with the click of a button, the first cell has a Select
component containing Object.keys(data)
.
The second cell contains another Select
component. I want this Select
component to contain parameters that are dependent on the value selected. For example, if /path/to/file1.csv
is selected, I want to make available those parameters (Parameter 1, Parameter 2, Parameter 3
), but if /path/to/file3.csv
is selected, I want to make available those parameters (Parameter 5, Parameter 6, Parameter 7
).
Here's my component:
import * as React from "react";
import PropTypes from "prop-types";
import { Button, Select, MenuItem } from "@mui/material";
import DeleteIcon from "@mui/icons-material/Delete";
import { DataGrid, GridActionsCellItem } from "@mui/x-data-grid";
const FileSelect = (props) => {
const { value } = props;
const [file, setFile] = React.useState("");
const handleChange = (event) => {
setFile(event.target.value);
};
return (
<Select id="file-select" value={file} onChange={handleChange} fullWidth>
{value?.map((item, index) => (
<MenuItem key={index} value={item}>
{item}
</MenuItem>
))}
</Select>
);
};
FileSelect.propTypes = {
value: PropTypes.array,
};
const ParameterSelect = (props) => {
const { value } = props;
const [parameter, setParameter] = React.useState("");
const handleChange = (event) => {
setParameter(event.target.value);
};
return (
<Select
id="parameter-select"
value={parameter}
onChange={handleChange}
fullWidth
>
{value?.map((item, index) => (
<MenuItem key={index} value={item}>
{item}
</MenuItem>
))}
</Select>
);
};
export default function DataGridTable(props) {
const { data } = props;
const files = Object.keys(data);
const [rows, setRows] = React.useState([]);
const columns = [
{
field: "file",
headerName: "File",
// width: 200,
flex: 1,
renderCell: FileSelect,
},
{
field: "x",
headerName: "X",
// width: 200,
flex: 0.5,
renderCell: ParameterSelect,
},
{
field: "actions",
headerName: "Delete",
type: "actions",
width: 80,
getActions: (params) => [
<GridActionsCellItem
icon={<DeleteIcon />}
label="Delete"
onClick={deleteRow(params.id)}
/>,
],
},
];
const handleClick = () => {
const newRow = {
id: rows.length + 1,
file: files,
x: [],
};
setRows((prevState) => [...prevState, newRow]);
};
const deleteRow = React.useCallback(
(id) => () => {
setTimeout(() => {
setRows((prevRows) => prevRows.filter((row) => row.id !== id));
});
},
[]
);
return (
<div>
<Button variant="contained" onClick={handleClick}>
Add row
</Button>
<div style={{ height: 300, width: "100%" }}>
<DataGrid rows={rows} columns={columns} disableSelectionOnClick />
</div>
</div>
);
}
The simplest way that I could think to accomplish this is by adding an extra field to the column definition as an "easy" place to store the selected value.
...
const FileSelect = (props) => {
const { value, row } = props;
const [file, setFile] = React.useState("");
const handleChange = (event) => {
setFile(event.target.value);
// Set the value here
row.selectedFile = event.target.value;
};
return (
<Select id="file-select" value={file} onChange={handleChange} fullWidth>
{value?.map((item, index) => (
<MenuItem key={index} value={item}>
{item}
</MenuItem>
))}
</Select>
);
};
...
{
field: "selectedFile",
hideable: true
},
...
Then set the selected value (file) in the FileSelect
parent value in the selectedFile
column. Then all that was left to do was to make the parameters
lookup values available to the ParameterSelect
. Again, I just stuffed them into the renderCell
props, but this could be done better as well:
...
{
field: "x",
headerName: "X",
flex: 0.5,
// Passing the entire original data in as an extra param, for demonstration purposes
renderCell: (props) => ParameterSelect({ ...props, data })
},
...
Finally, just hide the selectedFile
column:
...
<DataGrid
rows={rows}
columns={columns}
disableSelectionOnClick
// Hiding the extra field
columnVisibilityModel={{
selectedFile: false
}}
/>
...
Producing this: (I changed your values to make them easier to read while I was working)
Working CodeSandBox: https://codesandbox.io/s/prod-sun-bdvcu0?file=/demo.js:842-854