You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在打开新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。具体步骤如下:

  1. 给Login Modal添加状态管理,控制其显示/隐藏
  2. 给Register Modal添加受控状态支持,允许外部控制其显示
  3. 在切换按钮的点击事件中,先关闭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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 16:03:09