Next.js中如何在Navbar组件内正确渲染Logout按钮处理用户会话
解决方案
核心思路
由于NavBarComponent是客户端组件(使用了usePathname),无法直接引入服务端组件AuthButtonServer,我们可以通过从服务端组件传递session到客户端组件,然后在客户端组件中直接使用已有的AuthButtonClient来解决问题——AuthButtonClient本身就是为客户端环境设计的,和NavBarComponent完全兼容。
步骤1:修改NavBar组件,传递session给NavBarComponent
在服务端的NavBar组件中,将已获取的session作为props传递给NavBarComponent:
import AuthButtonServer from "@/app/auth/AuthButton/AuthButtonServer"; import Link from "next/link"; import NavbarComponent from "./NavBarComponent"; const NavBar = async ({ session} : {session: any}) => { return ( <div> {session ? ( <NavbarComponent session={session} /> {/* 传递session参数 */} ) : ( <>You must login first</> )} <AuthButtonServer/> </div> ); } export default NavBar;
步骤2:修改NavBarComponent,引入并使用AuthButtonClient
- 添加
'use client'声明确认客户端组件身份 - 接收
sessionprops并指定类型 - 引入
AuthButtonClient替换原硬编码的Logout按钮
修改后的代码:
'use client'; import { usePathname } from 'next/navigation'; import { Disclosure } from '@headlessui/react'; import { XMarkIcon, Bars3Icon } from '@heroicons/react/24/outline'; import AuthButtonClient from '@/app/auth/AuthButton/AuthButtonClient'; import { Session } from '@supabase/auth-helpers-nextjs'; // 补充导航数组(如果未定义) const navigation = [ { name: 'Home', href: '/' }, { name: 'Profile', href: '/profile' }, ]; export default function NavbarComponent({ session }: { session: Session | null }) { const pathname = usePathname(); return ( <Disclosure as="nav" className="bg-gray-800"> {({ open }) => ( <> <div className="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8"> <div className="relative flex h-16 items-center justify-between"> <div className="absolute inset-y-0 left-0 flex items-center sm:hidden"> <Disclosure.Button className="relative inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"> <span className="absolute -inset-0.5" /> <span className="sr-only">Open main menu</span> {open ? ( <XMarkIcon className="block h-6 w-6" aria-hidden="true" /> ) : ( <Bars3Icon className="block h-6 w-6" aria-hidden="true" /> )} </Disclosure.Button> </div> <div className="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start"> <div className="flex flex-shrink-0 items-center"> <img className="h-8 w-auto" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company" /> </div> <div className="hidden sm:ml-6 sm:block"> <div className="flex space-x-4"> {navigation.map((item) => ( <a key={item.name} href={item.href} className={ item.href === pathname ? 'bg-gray-900 text-white rounded-md px-3 py-2 text-sm font-medium' : 'text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium' } aria-current={item.href === pathname ? 'page' : undefined} > {item.name} </a> ))} </div> </div> </div> <div className="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0"> {/* 替换硬编码按钮为AuthButtonClient */} <AuthButtonClient session={session} /> </div> </div> </div> <Disclosure.Panel className="sm:hidden"> <div className="space-y-1 px-2 pb-3 pt-2"> {navigation.map((item) => ( <Disclosure.Button key={item.name} as="a" href={item.href} className={ item.href === pathname ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white' + ' block rounded-md px-3 py-2 text-base font-medium' } aria-current={item.href === pathname ? 'page' : undefined} > {item.name} </Disclosure.Button> ))} {/* 移动端也添加AuthButtonClient */} <div className="pt-2"> <AuthButtonClient session={session} /> </div> </div> </Disclosure.Panel> </> )} </Disclosure> ) }
步骤3:修复AuthButtonClient的错误
原代码中Login按钮错误绑定了handleSignOut事件,需要移除:
'use client'; import { Session, createClientComponentClient } from '@supabase/auth-helpers-nextjs'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; export default function AuthButtonClient({ session } : {session: Session | null}) { const supabase = createClientComponentClient(); const router = useRouter(); const handleSignOut = async () => { const { error } = await supabase.auth.signOut(); router.refresh(); if (error) { // eslint-disable-next-line no-console console.error('ERROR:', error); } } return session ? ( <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full" type="button" onClick={handleSignOut} > Sign Out </button> ) : ( <Link href="/login"> {/* 移除错误的onClick绑定 */} <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full" type="button"> Login </button> </Link> ) }
可选优化
如果不需要在NavBar组件底部单独显示AuthButtonServer,可以将其移除,因为NavBarComponent中已经包含了完整的登录/登出按钮逻辑。
内容的提问来源于stack exchange,提问作者JS3
相关产品推荐
相关产品推荐

