I'm using Material UI for a context menu in a react app. I've made a min repro here:
export default function App() {
const [contextMenu, setContextMenu] = React.useState(null);
const myArray = ["item1", "item2", "item3"];
const handleContextMenu = (event) => {
event.preventDefault();
setContextMenu(
contextMenu === null
? {
mouseX: event.clientX + 2,
mouseY: event.clientY - 6
}
: null
);
};
const editItem = (i) => {
setContextMenu(null);
alert(`Editing ${i}`);
};
return (
<div>
{myArray.map((i) => (
<div key={i} onContextMenu={handleContextMenu}>
<Card sx={{ backgroundColor: "gray", m: 2 }}>
<CardContent>
<Typography>{i}</Typography>
</CardContent>
</Card>
<Menu
open={contextMenu != null}
onClose={() => setContextMenu(null)}
anchorReference="anchorPosition"
anchorPosition={
contextMenu != null
? { top: contextMenu.mouseY, left: contextMenu.mouseX }
: undefined
}
>
<MenuItem onClick={() => editItem(i)}>Edit</MenuItem>
</Menu>
</div>
))}
</div>
);
}
Every time I right-click > "Edit", the alert always shows for the last item ("item3"). In my full app, I can also drag-drop to rearrange these cards and the behavior will still be whatever card is last in the list after the rearrange.
Not sure what I'm doing wrong here. I thought it might be a missing key issue, but I don't think that's the case as the only list item with siblings (the div
) has a key. Any ideas?
If you inspect your page, you can see that all 3 menus open at the same time, and the last one always receives the click event.
Here is how you can fix your code:
import "./styles.css";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Typography from "@mui/material/Typography";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import * as React from "react";
export default function App() {
const [contextMenu, setContextMenu] = React.useState(null);
const myArray = ["item1", "item2", "item3"];
const handleContextMenu = (item) => (event) => {
event.preventDefault();
setContextMenu(
contextMenu === null
? {
item,
mouseX: event.clientX + 2,
mouseY: event.clientY - 6
}
: null
);
};
const editItem = () => {
alert(`Editing ${contextMenu.item}`);
setContextMenu(null);
};
return (
<div>
{myArray.map((i) => (
<div key={i} onContextMenu={handleContextMenu(i)}>
<Card sx={{ backgroundColor: "gray", m: 2 }}>
<CardContent>
<Typography>{i}</Typography>
</CardContent>
</Card>
</div>
))}
<Menu
open={contextMenu != null}
onClose={() => setContextMenu(null)}
anchorReference="anchorPosition"
anchorPosition={
contextMenu != null
? { top: contextMenu.mouseY, left: contextMenu.mouseX }
: undefined
}
>
<MenuItem onClick={editItem}>Edit</MenuItem>
</Menu>
</div>
);
}