Material UI - Render / Open Menu Dynamically

Viewed 106

I'm trying to render a Material UI Menu for each of my JSX Button Elements.

Right now all of my Menu Items are stacked over each other. I would be able to only show the Menu which relates to the Button that has been clicked.

See it in Action

export default function App() {
  const [anchorEl, setAnchorEl] = useState(null);
  const menuOptions = [
    { id: 0, name: "Remove", method: "remove" },
    { id: 1, name: "Duplicate", method: "duplicate" },
    { id: 2, name: "New", method: "addNew" }
  ];

  const [addressInputFields, setAddressInputFields] = useState([
    { firstName: "", lastName: "" },
    { firstName: "", lastName: "" },
    { firstName: "", lastName: "" },
    { firstName: "", lastName: "" }
  ]);

  const handleMenu = (event) => {
    setAnchorEl(event.currentTarget);
  };

  const handleMenuClose = (event) => {
    setAnchorEl(null);
  };

  return (
    <div>
      {addressInputFields.map((input, index) => (
        <span key={index}>
          <button type="button" name="address" onClick={handleMenu}>
            ShowMenu
          </button>
          <Menu
            id="option-menu"
            anchorEl={anchorEl}
            open={Boolean(anchorEl)}
            onClose={handleMenuClose}
            anchorOrigin={{
              vertical: "bottom",
              horizontal: "left"
            }}
            getContentAnchorEl={null}
          >
            <span>Menu Index: {index}</span>
            {menuOptions.map((option) => (
              <MenuItem key={option.id}>{option.name}</MenuItem>
            ))}
          </Menu>
        </span>
      ))}
    </div>
  );
}

1 Answers

You can track the currently opened Menu by its index, then only render the menu with the open props set to true based on both Boolean(anchorEl) and the open index like below:

const [anchorEl, setAnchorEl] = useState(null);

// track with menu should be opened
const [openIndex, setOpenIndex] = useState(-1);

const handleMenu = (index) => (event) => {
  setAnchorEl(event.currentTarget);
  setOpenIndex(index); // set current menu index to open
};

const handleMenuClose = (event) => {
  setAnchorEl(null);
};

return (
  <div>
    {addressInputFields.map((input, index) => (
      <span key={index}>
        <button
          name="address"
          // handleMenu now need the index context to know which menu to open
          onClick={handleMenu(index)}
        >
          ShowMenu
        </button>
        <Menu
          // only render currently open menu
          open={Boolean(anchorEl) && index === openIndex}
          onClose={handleMenuClose}
          {...props}
        >
          <span>Menu Index: {index}</span>
          {menuOptions.map((option) => (
            <MenuItem key={option.id}>{option.name}</MenuItem>
          ))}
        </Menu>
      </span>
    ))}
  </div>
);

Live Demo

Edit 67211084/material-ui-render-open-menu-dynamically

Related