如何通过头像触发登录模态框?模态框切换及useState失效问题求助
解决方案
问题出在你直接在LoginModal内部嵌套RegisterModal,两个shadcn/ui的Dialog组件各自维护自己的显示状态,导致切换时无法联动关闭登录框。需要将两个模态框的状态统一提到父组件管理,具体修改步骤如下:
1. 修改LoginModal组件,接收外部状态和回调
将LoginModal改为受控组件,不再依赖Dialog内部的状态,而是接收父组件传递的open、onClose和切换到注册框的回调onSwitchToRegister:
"use client"; import Link from "next/link"; import { Dialog, DialogContent } 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 { LoginForm } from "@/components/auth/login-form"; interface LoginModalProps { open: boolean; onClose: () => void; onSwitchToRegister: () => void; } export function LoginModal({ open, onClose, onSwitchToRegister }: LoginModalProps) { return ( <Dialog open={open} onOpenChange={onClose}> <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">登录</p> <p className="text-muted-foreground text-sm"> 登录以访问XXXXXXX的专属内容 </p> </div> </div> </CardHeader> <CardContent> <LoginForm /> </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> <button className={cn( buttonVariants({ variant: "link", size: "sm", }), "text-secondary-foreground font-normal w-full items-start justify-start" )} onClick={() => { onClose(); // 先关闭登录框 onSwitchToRegister(); // 再打开注册框 }} > 还没有账号? </button> <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> ); }
2. 修改RegisterModal为受控组件
同样将RegisterModal改为受控组件,接收open和onClose props(假设你的RegisterModal结构类似LoginModal):
"use client"; import { Dialog, DialogContent } from "@/components/ui/dialog"; // 导入其他需要的组件 interface RegisterModalProps { open: boolean; onClose: () => void; } export function RegisterModal({ open, onClose }: RegisterModalProps) { return ( <Dialog open={open} onOpenChange={onClose}> <DialogContent className="p-0 w-auto bg-transparent border-none"> {/* 注册表单的内容,参考LoginModal的结构实现 */} </DialogContent> </Dialog> ); }
3. 在首页组件中统一管理状态
在首页(比如page.tsx)中使用useState管理两个模态框的显示状态,点击头像打开登录框,点击切换按钮触发状态切换:
"use client"; import { useState } from "react"; import { LoginModal } from "@/components/auth/login-modal"; import { RegisterModal } from "@/components/auth/register-modal"; import { Icons } from "@/components/icons"; import { Button } from "@/components/ui/button"; export default function Home() { const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); const [isRegisterModalOpen, setIsRegisterModalOpen] = useState(false); return ( <div className="min-h-screen flex items-center justify-end p-4"> {/* 首页头像触发按钮 */} <Button variant="ghost" onClick={() => setIsLoginModalOpen(true)} className="rounded-full" > <Icons.user className="h-6 w-6" /> </Button> {/* 登录模态框 */} <LoginModal open={isLoginModalOpen} onClose={() => setIsLoginModalOpen(false)} onSwitchToRegister={() => setIsRegisterModalOpen(true)} /> {/* 注册模态框 */} <RegisterModal open={isRegisterModalOpen} onClose={() => setIsRegisterModalOpen(false)} /> </div> ); }
这样修改后,点击头像会打开登录模态框,点击“还没有账号?”时会先关闭登录框,再打开注册框,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Evan Ethans
相关产品推荐
相关产品推荐

