NextJS14.2服务端子组件Dropdown登出后不动态刷新的解决
问题描述
- 环境:NextJS v14.2、React v18.3
- 通过
layout.tsx全局挂载服务端组件Navbar,其包含基于ShadCN UI的服务端子组件Dropdown - 登录流程正常:用户登录后跳转至成员页面,
Navbar/Dropdown会根据用户角色(管理员/普通用户)自动刷新内容 - 登出异常:调用
route.ts的GET接口完成登出并跳转至首页/后,控制台显示Navbar已触发,但Dropdown未更新为未登录状态(如显示“未登录用户”、isAdmin=false),仅手动刷新首页可恢复正常 - 已尝试无效方案:在
route.ts中添加revalidate('/')、在首页设置动态渲染headers
相关代码
navbar.tsx
import Link from 'next/link' import './navbar.css' import DropDown from './dropdown' import GetUser from '@/app/components/getUser' /* NAVBAR links to history, draw and members list pages gets user from layout.tsx and forwards data to dropdown component */ type typeNavProps = { name: string, loggedEmail: string, isLoggedOn: boolean, isAdmin: boolean } export default async function Navbar() { const navBarProps: typeNavProps = await GetUser() console.log('Fired NavBar with user: ', navBarProps.name) return ( <nav className="navbar"> <div className="navbar__container"> <Link href="/login" id="navbar__logo">The Slade</Link> <div className="navbar__toggle" id="mobile-menu"> <span className="bar"></span> <span className="bar"></span> <span className="bar"></span> </div> <ul className="navbar__menu"> <li className="navbar__item"> <Link href="/history" className="navbar__links">History</Link> </li> <li className="navbar__item"> <Link href="/draw" className="navbar__links">Draw</Link> </li> <li className="navbar__item"> <Link href="/members" className="navbar__links">Members List</Link> </li> <li> <DropDown name={navBarProps.name} loggedEmail={navBarProps.loggedEmail} isLoggedOn={navBarProps.isLoggedOn} isAdmin={navBarProps.isAdmin} /> </li> </ul> </div> </nav> ) }
dropdown.tsx
import Link from 'next/link' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, //DropdownMenuLabel, //DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" type typeNavProps = { name: string, loggedEmail: string, isLoggedOn: boolean, isAdmin: boolean } /* DROPDOWN BOX is from shadui gets user data from layout.tsx via navbar using navBarProps displays name of user and cuts available options if not an admin */ export default function DropDown(navBarProps: typeNavProps) { console.log('Fired dropdown with props from navbar: ', navBarProps) if (navBarProps.isAdmin) { return ( <> <DropdownMenu> <DropdownMenuTrigger>{navBarProps.name}</DropdownMenuTrigger> <DropdownMenuContent className='mt-10'> <DropdownMenuItem> <Link href="/login" >Log In</Link> </DropdownMenuItem> <DropdownMenuItem> <Link href="/logout" >Log Out</Link> </DropdownMenuItem> <DropdownMenuItem> <Link href="/updatemail" >Update Email</Link> </DropdownMenuItem> <DropdownMenuItem> <Link href="/updatepassword" >Update Password</Link> </DropdownMenuItem> <DropdownMenuItem> <Link href="/updateadmin" >Update Admin</Link> </DropdownMenuItem> <DropdownMenuItem> <Link href="/updateactive" >Update Active</Link> </DropdownMenuItem> <DropdownMenuItem> <Link href="/emailinvite" >Send Email Invitation</Link> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </> ) } else { return ( <> <DropdownMenu> <DropdownMenuTrigger>{navBarProps.name}</DropdownMenuTrigger> <DropdownMenuContent className='mt-10'> <DropdownMenuItem> <Link href="/login" >Log In</Link> </DropdownMenuItem> <DropdownMenuItem> <Link href="/logout" >Log Out</Link> </DropdownMenuItem> <DropdownMenuItem> <Link href="/updatemail" >Update Email</Link> </DropdownMenuItem> <DropdownMenuItem> <Link href="/updatepassword" >Update Password</Link> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </> ) } }
route.ts(登出接口)
import { redirect } from "next/navigation" import { createClient } from '@/app/utils/supabase/server' import { NextApiRequest } from "next" export async function GET(req: NextApiRequest) { console.log('logoutUser was fired with: ', req) const supabase = createClient() const { error } = await supabase.auth.signOut() if (error) { console.log('error logging out in logout route: ', error) } redirect('/') }
解决方案
1. 修复登出路由的缓存清除逻辑
NextJS 14.2中revalidate('/')已被弃用,改用revalidatePath清除布局和首页的缓存,确保登出后服务端重新渲染组件:
import { redirect, revalidatePath } from "next/navigation" import { createClient } from '@/app/utils/supabase/server' export async function GET() { const supabase = createClient() const { error } = await supabase.auth.signOut() if (error) { console.log('error logging out in logout route: ', error) } // 清除首页和全局布局的缓存 revalidatePath('/', 'layout') revalidatePath('/') redirect('/') }
2. 强制Navbar动态渲染
服务端组件默认静态优化,可能复用旧缓存数据。给Navbar添加动态渲染指令,确保每次请求都重新获取用户状态:
// 在navbar.tsx顶部添加 export const dynamic = "force-dynamic" export default async function Navbar() { // 原有代码不变 }
3. 修正GetUser函数的缓存策略
检查GetUser函数的缓存设置,确保每次都从Supabase拉取最新用户数据:
// 修改GetUser函数的缓存配置 export async function GetUser() { const supabase = createClient() // 禁用缓存,强制获取最新用户状态 const { data: { user } } = await supabase.auth.getUser({ cache: 'no-store' }) // 原有逻辑处理name、isAdmin等字段 }
4. 客户端兜底刷新(可选)
如果服务端缓存问题仍存在,可将登出改为客户端操作,触发路由刷新:
// 替换Dropdown中的Logout链接为客户端组件按钮 'use client' import { useRouter } from 'next/navigation' import { createClientComponentClient } from '@/app/utils/supabase/client' import { DropdownMenuItem } from "@/components/ui/dropdown-menu" export default function LogoutButton() { const router = useRouter() const handleLogout = async () => { const supabase = createClientComponentClient() await supabase.auth.signOut() // 触发客户端路由刷新,重新获取服务端组件数据 router.refresh() } return ( <DropdownMenuItem onClick={handleLogout}> Log Out </DropdownMenuItem> ) }
内容的提问来源于stack exchange,提问作者SgtBilko
相关产品推荐
相关产品推荐

