I use shadcn in my next.js 13 project. I want to have a dropdown with the option to edit or delete an entry. When the user clicks on "delete" a dialog should pop up and ask them for a confirmation. However, the dialog only shows for about 0.5 seconds before it closes together with the dropdown. How can I prevent that from happening?
Here is the example on codesandbox: Codesandbox
This is the code:
<DropdownMenu>
<DropdownMenuTrigger>
<p>Trigger</p>
</DropdownMenuTrigger>
<DropdownMenuContent>
<Dialog>
<DropdownMenuLabel>Edit Entry</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => conosle.log("Navigate to edit page")}
>
Edit
</DropdownMenuItem>
<DialogTrigger>
<DropdownMenuItem>Delete</DropdownMenuItem>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure?</DialogTitle>
<DialogDescription>
Do you want to delete the entry? Deleting this entry cannot be
undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</DropdownMenuContent>
</DropdownMenu>
When you click any <DropdownMenuItem />
, It will trigger the action (onClick) and close (unmount) the <DropdownMenuContent />
which includes the <DialogContent />
so it'll be unmounted with it.
<DialogContent />
outside of the <DropdownMenuContent />
// ...
export default function App() {
return (
<Dialog> {/* 🔴 The dialog provider outside of the DropdownMenuContent */}
<DropdownMenu>
<DropdownMenuTrigger>
<p>Trigger</p>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DialogTrigger>
Open Popup
</DialogTrigger>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{/* 🔴 DialogContent ouside of DropdownMenuContent */}
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure?</DialogTitle>
<DialogDescription>
Do you want to delete the entry? Deleting this entry cannot be
undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
This solution works well if you have a single item triggers dialog. But what if you have multiple dialogs?
Move your dialog outside the <DropdownMenuContent />
, create a state for each one:
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
then remove any <DialogTrigger />
, add onClick instead
<DropdownMenuItem onClick={() => setIsEditDialogOpen(true)}>Edit</DropdownMenuItem>
<DropdownMenuItem onClick={() => setIsDeleteDialogOpen(true)}>Delete</DropdownMenuItem>
In your dialog add
<Dialog open={isEditDialogOpen || isDeleteDialogOpen}
onOpenChange={isEditDialogOpen ?
setIsEditDialogOpen : setIsDeleteDialogOpen}>
...
</Dialog>
If you don't want to make it controlled and can render two trigger buttons, you can render two separate dialogs:
<Dialog>
<DialogTrigger>Edit Post</DialogTrigger>
<DialogContent>
Content
</DialogContent>
</Dialog>
<Dialog>
<DialogTrigger>Edit Post</DialogTrigger>
<DialogContent>
Content
</DialogContent>
</Dialog>