如何在打开新Dialog前关闭当前Dialog?(登录/注册弹窗场景)
问题:点击Register Modal时Login Modal未关闭,叠加显示
我将“没有账户?”按钮嵌套在Register Modal组件中,Register Modal的结构与Login Modal完全一致。但点击该按钮打开Register Modal时,Login Modal并未关闭,而是叠加显示。我希望在打开Register Modal前先关闭当前的Login Modal。
我的Login Modal代码
"use client"; import Link from "next/link"; import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; import { Card, CardContent, CardFooter, CardHeader, } from "@/components/ui/card"; import { Icons } from "@/components/icons"; import { siteConfig } from "@/config/site"; import { Button, buttonVariants } from "@/components/ui/button"; import { ModeToggle } from "../mode-toggle"; import { cn } from "@/lib/utils"; import { RegisterModal } from "@/components/auth/register-modal"; interface LoginModalProps { children: React.ReactNode; asChild?: boolean; } export function LoginModal({ children, asChild }: LoginModalProps) { return ( <Dialog> <DialogTrigger asChild={asChild}>{children}</DialogTrigger> <DialogContent className="p-0 w-auto bg-transparent border-none"> <Card className="w-[400px] shadow-md"> <CardHeader> <div className="w-full flex flex-col gap-y-4 items-start justify-start"> <Link href="/"> <div className="mr-6 flex items-center space-x-2"> <Icons.logo className="h-6 w-6" /> <span className="hidden font-bold sm:inline-block"> {siteConfig.name} </span> </div> </Link> <div className="gap-y-0.5"> <p className="text-3xl font-bold">Login</p> <p className="text-muted-foreground text-sm"> to access exclusive content on XXXXXX </p> </div> </div> </CardHeader> <CardContent>Login Form!</CardContent> <CardFooter> <div className="flex items-center w-full gap-x-2"> <Button size="lg" className="w-full" variant="outline" onClick={() => {}} > <Icons.google className="h-5 w-5" /> </Button> <Button size="lg" className="w-full" variant="outline" onClick={() => {}} > <Icons.discord className="h-5 w-5" /> </Button> </div> </CardFooter> <CardFooter> <div className={cn( buttonVariants({ variant: "link", size: "sm", }), "text-secondary-foreground font-normal w-full items-start justify-start" )} onClick={onClose} > <RegisterModal> Don't have an account? </RegisterModal> </div> <div className="flex flex-1 items-center space-x-2 justify-end"> <nav className="flex items-center"> <Link href={siteConfig.links.discord} target="_blank" rel="noreferrer" > <div className={cn( buttonVariants({ variant: "ghost", }), "w-9 px-0" )} > <Icons.discord className="h-4 w-4" /> <span className="sr-only">Discord</span> </div> </Link> <Link href={siteConfig.links.twitter} target="_blank" rel="noreferrer" > <div className={cn( buttonVariants({ variant: "ghost", }), "w-9 px-0" )} > <Icons.twitter className="h-3 w-3 fill-current" /> <span className="sr-only">Twitter</span> </div> </Link> <ModeToggle /> </nav> </div> </CardFooter> </Card> </DialogContent> </Dialog> ); }
解决方案
核心思路是手动控制两个Modal的显示状态,在触发Register Modal前先关闭Login Modal。具体步骤如下:
- 给Login Modal添加状态管理,控制其显示/隐藏
- 给Register Modal添加受控状态支持,允许外部控制其显示
- 在切换按钮的点击事件中,先关闭Login Modal,再打开Register Modal
修改后的LoginModal代码
"use client"; import { useState } from "react"; import Link from "next/link"; import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; import { Card, CardContent, CardFooter, CardHeader, } from "@/components/ui/card"; import { Icons } from "@/components/icons"; import { siteConfig } from "@/config/site"; import { Button, buttonVariants } from "@/components/ui/button"; import { ModeToggle } from "../mode-toggle"; import { cn } from "@/lib/utils"; import { RegisterModal } from "@/components/auth/register-modal"; interface LoginModalProps { children: React.ReactNode; asChild?: boolean; } export function LoginModal({ children, asChild }: LoginModalProps) { // 控制Login Modal的显示状态 const [isLoginOpen, setIsLoginOpen] = useState(false); // 控制Register Modal的显示状态 const [isRegisterOpen, setIsRegisterOpen] = useState(false); const handleSwitchToRegister = () => { // 先关闭Login Modal setIsLoginOpen(false); // 延迟打开Register Modal,确保动画完成 setTimeout(() => setIsRegisterOpen(true), 200); }; return ( <> <Dialog open={isLoginOpen} onOpenChange={setIsLoginOpen}> <DialogTrigger asChild={asChild}>{children}</DialogTrigger> <DialogContent className="p-0 w-auto bg-transparent border-none"> <Card className="w-[400px] shadow-md"> <CardHeader> <div className="w-full flex flex-col gap-y-4 items-start justify-start"> <Link href="/"> <div className="mr-6 flex items-center space-x-2"> <Icons.logo className="h-6 w-6" /> <span className="hidden font-bold sm:inline-block"> {siteConfig.name} </span> </div> </Link> <div className="gap-y-0.5"> <p className="text-3xl font-bold">Login</p> <p className="text-muted-foreground text-sm"> to access exclusive content on XXXXXX </p> </div> </div> </CardHeader> <CardContent>Login Form!</CardContent> <CardFooter> <div className="flex items-center w-full gap-x-2"> <Button size="lg" className="w-full" variant="outline" onClick={() => {}} > <Icons.google className="h-5 w-5" /> </Button> <Button size="lg" className="w-full" variant="outline" onClick={() => {}} > <Icons.discord className="h-5 w-5" /> </Button> </div> </CardFooter> <CardFooter> <div className={cn( buttonVariants({ variant: "link", size: "sm", }), "text-secondary-foreground font-normal w-full items-start justify-start cursor-pointer" )} onClick={handleSwitchToRegister} > Don't have an account? </div> <div className="flex flex-1 items-center space-x-2 justify-end"> <nav className="flex items-center"> <Link href={siteConfig.links.discord} target="_blank" rel="noreferrer" > <div className={cn( buttonVariants({ variant: "ghost", }), "w-9 px-0" )} > <Icons.discord className="h-4 w-4" /> <span className="sr-only">Discord</span> </div> </Link> <Link href={siteConfig.links.twitter} target="_blank" rel="noreferrer" > <div className={cn( buttonVariants({ variant: "ghost", }), "w-9 px-0" )} > <Icons.twitter className="h-3 w-3 fill-current" /> <span className="sr-only">Twitter</span> </div> </Link> <ModeToggle /> </nav> </div> </CardFooter> </Card> </DialogContent> </Dialog> {/* 受控渲染Register Modal */} <RegisterModal open={isRegisterOpen} onOpenChange={setIsRegisterOpen} > <span></span> </RegisterModal> </> ); }
对应的RegisterModal修改(支持受控状态)
"use client"; import { useState } from "react"; // 导入你的其他组件和资源 interface RegisterModalProps { children: React.ReactNode; asChild?: boolean; open?: boolean; onOpenChange?: (open: boolean) => void; } export function RegisterModal({ children, asChild, open: propOpen, onOpenChange: propOnOpenChange }: RegisterModalProps) { // 优先使用外部传入的受控状态,否则使用内部状态 const [internalOpen, setInternalOpen] = useState(false); const open = propOpen ?? internalOpen; const onOpenChange = propOnOpenChange ?? setInternalOpen; return ( <Dialog open={open} onOpenChange={onOpenChange}> <DialogTrigger asChild={asChild}>{children}</DialogTrigger> <DialogContent className="p-0 w-auto bg-transparent border-none"> <Card className="w-[400px] shadow-md"> <CardHeader> {/* 替换为Register相关的头部内容 */} <div className="w-full flex flex-col gap-y-4 items-start justify-start"> <Link href="/"> <div className="mr-6 flex items-center space-x-2"> <Icons.logo className="h-6 w-6" /> <span className="hidden font-bold sm:inline-block"> {siteConfig.name} </span> </div> </Link> <div className="gap-y-0.5"> <p className="text-3xl font-bold">Register</p> <p className="text-muted-foreground text-sm"> Create your account to access exclusive content </p> </div> </div> </CardHeader> <CardContent>Register Form!</CardContent> {/* 其余内容保持和LoginModal一致,按需调整文案 */} </Card> </DialogContent> </Dialog> ); }
修改后,点击切换按钮时会先关闭Login Modal,延迟片刻后打开Register Modal,避免两个Modal叠加显示的问题。
内容的提问来源于stack exchange,提问作者Evan Ethans
相关产品推荐
相关产品推荐

