Max Carroll
Max Carroll

Reputation: 4839

Generating groupings within a Material UI Select component dynamically from data

I need to generate the groupings of my Select component dynamically and would like to use the component in a controlled way. (As opposed to uncontrolled.)

This code snippet works fine without the <ListSubheader ...> component, however I need it and the Material UI Docs example for group select shows using the <ListSubheader ...> component in this way.

  <Select fullWidth value={selectedPlan} onChange={handleChange}>
        {products?.map(p => (
          <>
            <ListSubheader>{p.name}</ListSubheader>
            {p.plans.map(pl => (
              <MenuItem key={pl.id} value={pl}>
                {pl.id} {pl.name} {pl.type} {pl.price}
              </MenuItem>
            ))}
          </>
        ))}
      </Select>

However it seems impossible to generate this dynamically if we are getting the error message

The Menu component doesn't accept a Fragment as a child. Consider providing an array instead.

According to the Material UI documentation,

⚠️The MenuItem elements must be direct descendants when native is false.

How can I programatically generate my groupings in my component.

I have created a code sandbox where this problem is reproducible

Upvotes: 10

Views: 10881

Answers (4)

gul_sos
gul_sos

Reputation: 21

This code works fine:

<Select fullWidth value={selectedPlan} onChange={handleChange}>
  {products?.reduce((acc, p) => {
    acc.push(
      <ListSubheader key={`subheader-${p.name}`}>{p.name}</ListSubheader>
    );
    acc.push(
      ...p.plans.map(pl => (
        <MenuItem key={pl.id} value={pl}>
          {pl.id} {pl.name} {pl.type} {pl.price}
        </MenuItem>
      ))
    );
    return acc;
  }, [])}
</Select>

Upvotes: 2

Tajs
Tajs

Reputation: 621

Try consider using Autocomplete which has more straightforward support for this kind of query

<Autocomplete
  multiple
  id="checkboxes-tags-demo"
  options={niche_data}
  groupBy={(option) => option.niche}
  onChange={(e, v) => {
    onChange(v);
  }}
  getOptionLabel={(option) => option.sub_niche}
  renderOption={(props, data) => (
    <Typography key={data.id} {...props} variant="body1">
      {data.sub_niche}
    </Typography>
  )}
  style={{ width: 500 }}
  renderInput={(params) => (
    <TextField
      {...params}
      variant="outlined"
      label="niches/Companies"
      placeholder="Relationships/Companies"
    />
  )}
/>;

Where data is

const niche_data = [
    {id:1,sub_niche:"CBD Oil",niche:"CBD"},
    {id:2,sub_niche:"CBD Pets",niche:"CBD"},
    {id:3,sub_niche:"CBD Skincare",niche:"CBD"},
    {id:4,sub_niche:"CBD Edibles",niche:"CBD"},
    {id:5,sub_niche:"CBD Vapes",niche:"CBD"},
    {id:6,sub_niche:"CBD Beauty",niche:"CBD"},
    {id:7,sub_niche:"Weightloss",niche:"Food supplements"},
    {id:8,sub_niche:"Sports nutrition",niche:"Food supplements"},
    {id:9,sub_niche:"Nootropic",niche:"Food supplements"},
    {id:11,sub_niche:"Vitamins",niche:"Food supplements"},
    {id:12,sub_niche:"Face oil",niche:"Skincare"},
    {id:13,sub_niche:"Facecream",niche:"Skincare"},
    {id:14,sub_niche:"Cleanser",niche:"Skincare"},
    {id:15,sub_niche:"Lip balm",niche:"Skincare"},
    {id:16,sub_niche:"Serum & Tones",niche:"Skincare"},
]

enter image description here

Upvotes: 0

Ederson Fajardo
Ederson Fajardo

Reputation: 11

I had the same problem with the selector and it helped me to replicate some of this code https://codesandbox.io/s/mui-select-grouping-problem-i9jtj?file=/src/App.js:1662-1704 if the component expects you to take a single array and not a group try a reduce() like this https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

Upvotes: 1

tdranv
tdranv

Reputation: 1330

Here is the edited sandbox.

Yes, the issue was that you had it wrapped in a fragment. If you wrap it in a <div> it works but returns undefined for event.target.value since <MenuItem/> is not a direct child of <Select/>.

P.S. I extracted it to a function just make it more clear. It has nothing to do with the fix.

Upvotes: 18

Related Questions