Reputation: 57
From existing data, I am generating a number of MUI "<Select>" elements dynamically. However when interacted with one of the "<Select>" elements, the selected value is applied to all "<Select>" elements.
For example, this is given data:
const groups = [{name: "group1",group_id: 1}, {name: "group2", group_id: 2}];
const options = [
{name: "option1", option_id: 1, group_id: 1},
{name: "option2", option_id: 2, group_id: 1},
{name: "option3", option_id: 3, group_id: 1},
{name: "option4", option_id: 4, group_id: 2},
{name: "option5", option_id: 5, group_id: 2},
{name: "option6", option_id: 6, group_id: 2},
]
Using this data, two "<Select>" elements are generated. Each has different 3 options. When I select using first "<Select>" element, value in second "<Select>" element also changes.
My code for generating elements:
const [selectedElements, setSelectedElements] = React.useState([]);
const onSelectionChange = (event, serviceGroup) => {
const value = event.target.value;
setSelectedElements(
typeof value === 'string' ? value.split(',') : value,
);
}
return (
<div>
{
groups.map(function(group) {
return (
<Select
multiple
value={selectedElements}
onChange={(e)=>onSelectionChange(e, group)}
id={group.group_id}
renderValue={(selected) => selected.join(', ')}
>
{
options.map(function (option) {
if (option.group_id === group.group_id) {
return (
<MenuItem
key={option.option_id}
value={option.name}
>
<ListItemText primary={option.name} />
<Checkbox checked={selectedElements.indexOf(option.name) > -1}/>
</MenuItem>
)
}
})
}
</Select>
)
})
}
</div>
)
I know that "value={selectedElements}" will cause every dynamically generated "<Select>" element have the same value, thus if one of them changes its value, others will change their value too.
To fix this, I tried using const [selectedElementsObj, setSelectedElementsObj] = React.useState({});
- an object state, and then replace "value={selectedElements}" with "value={selectedElementsObj[group.name]}".
While inside the onSelectionChange function, I created an object of arrays, so that inside selectedElementsObj I will have separate array for each "<Select" element:
const objCopy = Object.assign({}, selectedElementsObj);
objCopy[group.name] = typeof value === 'string' ? value.split(',') : value;
setSelectedElementsObj(objCopy);
The problem with my attempt is that it just not renders and I get white screen. I hope I described my problem clearly, and here is codesandbox with the example i have shown here !
Upvotes: 1
Views: 2309
Reputation: 82
The problem here is that onChange event, you were not setting value for that particular item (as you also said). So to identify each element differently there has to have certain key in selectedElements
or selectedElementsObj
based on which we can know this value is to be set for that certain id.
To solve this what I did was setting seletedElements
with groups array of obj.
const [selectedElements, setSelectedElements] = React.useState(groups);
And then used this to map over Select component, and changed its value based selectedElements value.
selectedElements.map(function (group) {
return (
<Select
style={{ marginBottom: "10px" }}
multiple
value={group?.value || []}
onChange={(e) => onSelectionChange(e, group)}
id={group.group_id}
renderValue={(selected) => selected.join(", ")}
sx={{
"& legend": { display: "none" },
"& fieldset": { top: 0 }
}}
>
{options.map(function (option) {
if (option.group_id === group.group_id) {
return (
<MenuItem key={option.option_id} value={option.name}>
<ListItemText primary={option.name} />
<Checkbox
checked={selectedElements.indexOf(option.name) > -1}
/>
</MenuItem>
);
}
})}
</Select>
);
})}
And the last part left was assigning (rather creating here) that value in that obj, for which I did this inside onChange function call:
selectedElements.map(item => {
if(item.group_id === group.group_id)
{
item.value = typeof value === "string" ? value.split(",") : value
}
})
Upvotes: 2