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>
You can prevent the default behavior of the onClick event which I believe is renamed to onSelect (https://www.radix-ui.com/primitives/docs/components/dropdown-menu) on DropdownMenuItem.
This way you can extract Dialog logic into a separate component and simply include it inside your Dropdown wrapper:
// {...}
<DropdownMenu>
<DropdownMenuTrigger>
<p>Trigger</p>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>Edit Entry</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem onSelect={() => console.log("Navigate to edit page")}>
Edit
</DropdownMenuItem>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
<YourCustomDialog />
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>;
// {...}
const YourCustomDialog = () => {
return (
<Dialog>
<DialogTrigger>Delete</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>
);
};
The drawback of this implementation is that the dropdown menu stays open while you interact with Dialog, though you can reuse YourCustomDialog component.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With