Here i'm using select from material ui, and have a question: how to get an object (handleChange function) when user clicks from dropdown ? in my handleChange i want to have object and not just 'name', and those places where i'm using 'selected' i could just pick it from object like this selected.name (if needed). and another question: i'm getting data to 'selected' from api the thing is that that data shows on select itself but nothing shows in dropdown. any advices how to make this code simpler?
import React, { useState } from "react";
import Checkbox from "@material-ui/core/Checkbox";
import InputLabel from "@material-ui/core/InputLabel";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";
import DeleteIcon from "@material-ui/icons/Delete";
import CreateIcon from "@material-ui/icons/Create";
import { MenuProps, useStyles } from "./utils";
import AddUser from "./AddUser";
import { Button } from "@material-ui/core";
import EditUser from "./EditUser";
function App() {
const rawOptions = [
{ id: 1, name: "Oliver Hansen" },
{ id: 2, name: "Van Henry" }
];
const classes = useStyles();
const [selected, setSelected] = useState([]);
const [options, setOptions] = useState(rawOptions);
const [openAddModal, setOpenAddModal] = useState(false);
const [openUpdateModal, setOpenUpdateModal] = useState(false);
const [edit, setEdit] = useState({
id: null,
name: ""
});
const handleChange = (event) => {
const value = event.target.value;
setSelected(value);
console.log("get id and name of selected here", value);
};
function addUser(newArray) {
const newTodos = [...options, newArray];
setOptions(newTodos);
}
const openAddUser = () => {
setOpenAddModal(true);
};
const openUpdateUser = (event, data) => {
event.preventDefault();
event.stopPropagation();
setOpenUpdateModal(true);
setEdit(data);
};
// console.log("id", edit);
const closeAddModal = () => {
setOpenAddModal(false);
};
const closeUpdateModal = () => {
setOpenUpdateModal(false);
};
const updateUser = (updateUser) => {
setOptions(
options.map((user) => (user.id === updateUser.id ? updateUser : user))
);
};
return (
<FormControl className={classes.formControl}>
<div>
<InputLabel id="mutiple-select-label">Multiple Select</InputLabel>
<Select
labelId="mutiple-select-label"
multiple
variant="outlined"
value={selected || []}
onChange={handleChange}
renderValue={(selected) => selected}
MenuProps={MenuProps}
>
{options.map((option, index) => (
<MenuItem key={index} value={option.name}>
<ListItemIcon>
<Checkbox checked={selected?.includes(option.name)} />
</ListItemIcon>
<ListItemText primary={option.name}>{option}</ListItemText>
<DeleteIcon
onClick={(e) => {
e.stopPropagation();
setOptions(options.filter((o) => o.id !== option.id));
}}
/>
<ListItemIcon>
<CreateIcon
onClick={(e) =>
openUpdateUser(e, { id: option.id, name: option.name })
}
/>
</ListItemIcon>
</MenuItem>
))}
</Select>
<Button onClick={openAddUser} style={{ backgroundColor: "#287B7A" }}>
Add User
</Button>
</div>
<p>{selected}</p>
<AddUser
openAddModal={openAddModal}
handleClose={closeAddModal}
array={options}
addUser={addUser}
/>
<EditUser
edit={edit}
openUpdateModal={openUpdateModal}
handleClose={closeUpdateModal}
array={options}
updateUser={updateUser}
/>
</FormControl>
);
}
export default App;
First change value
to the id
<MenuItem key={option.id} value={option.id}>
Since you need that to be guaranteed to be unique.
And also change the checkbox:
<Checkbox checked={selected?.includes(option.id)} />
Then you will store an array of ids in the selected
state.
const handleChange = (event) => {
setSelected(event.target.value);
};
And if you ever need to get back to the options (it would be bad to store the whole option because it'd be duplicated state) you can do:
// If after the handlechange fn
selected.map((selectedId) => options.find(option => option.id === selectedId))
// If inside the handlechange fn
event.target.value.map((selectedId) => options.find(option => option.id === selectedId))
Also change renderValue
to show the names in the box when they are selected:
renderValue={(selected) =>
selected
.map(
(selectedId) =>
options.find((option) => option.id === selectedId)?.name
)
.join(", ")
}
You might want to break that map/find statement into a common function.
You also need to change the delete handler to clear out selected items that have been deleted:
<DeleteIcon
onClick={(e) => {
e.stopPropagation();
setSelected((prev) => prev.filter((id) => id !== option.id));
setOptions(options.filter((o) => o.id !== option.id));
}}
/>
As for the selected items being fetched from the server and them being a list of names, you will need to work back from the names to the id.
const initialSelectedValues = selectedNamesFromServer.map((selectedName) => options.find(option => option.name === selectedName).id)
Note this seems a little like a bad API design since you should always use ID only. What happens if there are 2 people with the same name? Probably you wouldn't be able to select the second one as it can not be disambiguated. The server should return ids really.
Presumably, you have endpoints to do the following. I have recommended what to do or each (I don't know your URL scheme):
id
and name
. This is what replaced rawArray
.