3
votes

Recently I tried to implement a simple dropdown menu using MaterialUI(4.3.3)'s Menu and MenuItem components.

The onclick event on the MenuItem is not behaving correctly. When console.log(id) is called, every component is echoing the last element from sample array. I switched to ListItem, the problem is gone.


const Test = () => {
    const [anchorEl, setAnchorEl] = React.useState(null);
    const sampleArray = ["test1", "test2", "test3"];
    const open = Boolean(anchorEl);

    return (
        sampleArray.map(id => {
            let curID = id;
            return (<>
                <IconButton
                    aria-label="more"
                    aria-controls="long-menu"
                    aria-haspopup="true"
                    onClick={event => setAnchorEl(event.currentTarget)}
                >
                    <MoreHorizIcon />
                </IconButton>
                <Menu
                    elevation={0}
                    id="long-menu"
                    anchorEl={anchorEl}
                    keepMounted
                    open={open}
                    transformOrigin={{
                        vertical: 'top',
                        horizontal: 'center',
                    }}
                    onClose={() => setAnchorEl(null)}
                >
                    <MenuItem onClick={() => {
                        setAnchorEl(null);
                        console.log(curID); // should be each individual id, but here always "test3"
                    }}>Test</MenuItem>
                </Menu>
            </>);
        })
    )
}

Is this the desired behavior for Menu? How to resolve it?

1
Try in map function before return add let curId=id; and use it in onclick instead.Oleg
@Oleg Still the last element is printed. :( I will edit to add this line. Thanks for the suggestion!Dan Lee
try to use Index from map second parameter and access by index to it . let curIndex=index;Oleg
Sorry, can you push let as you did before in click function and not before returnOleg

1 Answers

6
votes

Yes, this is the desired behavior. It's a little confusing:
You have single state (anchorEl), which decides what is the anchor for all menus.
When you click on a IconButton, you set the same AnchorEl to all menus.
So when you open the menu, you actually open all 3 menus in the same place!
The reason you always get 'test3' in the console, is because menu with the id test3 is rendered last and he overlaps the other menus.


To fix this, you need every menu to manage it's own state, so every menu will have it's own anchor:

const IsolatedMenu = props => {
  const [anchorEl, setAnchorEl] = React.useState(null);
  const open = Boolean(anchorEl);

  return(
  <React.Fragment>
    <IconButton
    aria-label="more"
    aria-controls="long-menu"
    aria-haspopup="true"
    onClick={event => setAnchorEl(event.currentTarget)}
    >
    <PriorityHighIcon />
    </IconButton>
    <Menu
    elevation={0}
    id="long-menu"
    anchorEl={anchorEl}
    keepMounted
    open={open}
    transformOrigin={{
        vertical: 'top',
        horizontal: 'center',
    }}
    onClose={() => setAnchorEl(null)}
    >
    <MenuItem onClick={() => {
        setAnchorEl(null);
        console.log('curr id',props.id); // should be each individual id, but here always "test3"
    }}>Test</MenuItem>
    </Menu>

  </React.Fragment>
  )
}

And than your Test component should look like that:

const Test = () => {
  const sampleArray = ["test1", "test2", "test3"];

  return (
      sampleArray.map(id => {
          return (<>
            <IsolatedMenu  id={id}/>
          </>);
      })
  )
}

Edit Invisible Backdrop