Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React render siblings on Multiple Select Material UI

This is my problem: I need to create a multiple select (with checkboxes) on React using material UI, just like the following image:

React multiple select with hierarchy Country/states

I have the data in an array like the following

data = [
  {
    id: 199,
    name: "Argentina",
    state: [
      { name: "Buenos Aires", id: 1 },
      { name: "Cordoba", id: 2 },
      { name: "Chaco", id: 2 }
    ]
  },
  {
    id: 200,
    name: "USA",
    state: [
      { name: "California", id: 4 },
      { name: "Florida", id: 5 },
      { name: "Texas", id: 6 }
    ]
  }
]

Perfect. Everything seems to have to do 2 nested maps, nothing out of the ordinary. Meanwhile, I try this:

<InputLabel htmlFor="grouped-select">Grouping</InputLabel>
<Select
  defaultValue={[]}
  id="grouped-select"
  onChange={handleChange}
  multiple
>
  <MenuItem value="">
    <Checkbox />
    <em>All</em>
  </MenuItem>
  {data.map(country => (
    <>
      <ListSubheader key={country.id}>{country.name}</ListSubheader>
      {country.state.map(state => (
        <MenuItem key={state.id} value={state.id}>
          <Checkbox />
          {state.name}
        </MenuItem>
      ))}
    </>
  ))}
</Select>

That renders the select well, makes it look nice. But my problem is that (apparently) material doesn't accept React.Fragment (<></>) inside select. Meanwhile, I can't generate the structure that Material UI specifies in the documentation looping through an array:

<ListSubheader>Parent 1</ListSubHeader>
<MenuItem key={state.id} value={state.id}>Child1</MenuItem>
<MenuItem key={state.id} value={state.id}>Child2</MenuItem>
<MenuItem key={state.id} value={state.id}>Child...n</MenuItem>

<ListSubheader>Parent 2</ListSubHeader>
<MenuItem key={state.id} value={state.id}>Child</MenuItem>
<MenuItem key={state.id} value={state.id}>Child...n</MenuItem>

If i put a <div> or other element instead of <></>, the onchange event stops working.

I also tried to use rendering with arrays, but did not have good results since the second sibling is not a jsx, but I have to loop through the array.

{data.map(country => [
    <ListSubheader key={country.id}>{country.name}</ListSubheader>
    {country.state.map(state => (
      <MenuItem key={state.id} value={state.id}>
        <Checkbox />
        {state.name}
      </MenuItem>
    ))}
  ])}

I am blocked, I do not know how it can be solved. I thought of a child component but I am going to have the same problem when rendering it.

Is there a way to solve it?

I leave a link with the example snippet: https://codesandbox.io/s/material-demo-h77i8

Thank you very much.

like image 630
Federico Saenz Avatar asked Sep 12 '25 00:09

Federico Saenz


1 Answers

You could check this codesandbox here.

What you can note from this answer is that you can always create elements without the need of fragments. As fragments are just the wrappers around array. Since, material doesn't want fragments in them. You could use pure array to render your elements instead of wrapping it into fragments.


function makeItems(data) {
  const items = [];
  for (let country of data) {
    items.push(<ListSubheader key={country.id}>{country.name}</ListSubheader>);
    for (let state of country.state) {
      items.push(
        <MenuItem key={state.id} value={state.id}>
          <Checkbox />
          {state.name}
        </MenuItem>
      );
    }
  }
  return items;
}

You can look how I used another function to push into javascript array and then render them to the tree.

like image 150
Dipesh Dulal Avatar answered Sep 13 '25 12:09

Dipesh Dulal