React: Edit and Delete from a pop-up menu on a list item

Viewed 1893

I have a list container component, which is the parent. It maps out the list rows. On the list row component, which is the child, every item has a button to toggle a pop-up menu, which has a button for edit, and a button for delete. The menu itself is a sibling to the list rows because if I include it in the list rows component, each row will render a menu and when toggled, they would all stack up on top of each other. The edit and delete buttons toggle either a form for the edit, or directly remove the item.

What I currently have is:

// Parent / Container

  const [itemID, setItemID] = useState(null);

  const handleMenuOpen = (id) => (e) => {
    setAnchorEl(e.currentTarget); // for menu placement
    setItemID(id);
  };

  const handleItemDelete = () => {
    dispatch(deleteItem(itemID));
  };

<List>
  <ListRow handleMenuOpen={handleMenuOpen} />
  <Menu handleItemDelete={handleItemDelete}  itemID={itemID} />
</List>;


// List Row

<Button onClick={handleMenuOpen(item.id)} />;

// Menu

<MenuItem onClick={() => handleModalOpen(itemID)} />;

<MenuItem onClick={() => handleItemDelete()} />;

The edit button works fine but no matter how I try, I cannot get setItemID to work from the onClick on the list item. It always come out as the initial value of null. I console logged that the ID in the function parameter came out properly but the setState hook did not work.

I tried putting the useState on the list item and pass the ID through useContext but came out undefined when handledItemDelete was called.

I tried using ref on the child to get the ID from the parent, which also came out as undefined.

I cannot think of how to use useEffect to check for a change in the handleMenuOpen parameter.

I am out of ideas. Anyone know what the issue is and how to fix it?

3 Answers

You should probably just pass the handleMenuOpen function and rely on the selected element and then store it's id in itemID variable.

const handleMenuOpen = (e) => {
    setAnchorEl(e.currentTarget); // for menu placement
    setItemID(e.currentTarget.id);
};
  
  
<MenuItem onClick={handleMenuOpen} />;

i had the same problem before. I think you should handle the popup toggling in the child component, so something like this.

function Parent() {
    function handleDelete(item) {
        deleteFunction(item.id)
    }

    return (
        <div>
            {[].map((item, index) => {
                return (
                    <ListRowItem key={index} handleDelete={handleDelete} item={item} />
                )
            })}
        </div>
    )
}

function ListRowItem({handleDelete, item}) {
    const [isMenuOpen, setIsMenuOpen] = useState(false)
    const [isModelVisible, setIsModalVisible] = useState(false)

    return (
        <div>
            <Button onClick={isMenuOpen === true ? () => setIsMenuOpen(true) : () => setIsMenuOpen(false)} />
            {isModelVisible === true ? <ModalItem /> :null}
            {isMenuOpen === true ? 
                <div>
                    <MenuItem onClick={() => setIsModalVisible(true)} />
                    <MenuItem onClick={() => handleDelete(item.id)} />
                </div>
            : null}
        </div>
    )
}

I assume you are doing a certain loop to render each List Row inside the List component

let's say all items are in an items array which you loop:

{items.map(item => (
  <ListRow handleMenuOpen={handleMenuOpen}/>
  <Menu handleItemDelete={handleItemDelete} item={item} />
)}

now in the Menu container or component, you would have the item and pass it to the Menu item

Related