Next.js 14登录/登出流程异常:跳转后头部显示错误
Next.js 14 登录/登出后头部状态不即时更新问题排查与解决
问题描述
我是Server Components新手,在Next.js 14中实现登录/登出功能,将access_token存储在cookie中。已编写登录路由处理、动态头部、checkToken服务端动作、登出服务端动作、首页及根布局代码,但登录后跳转首页、点击登出时头部显示错误,刷新浏览器后才恢复正常,不清楚问题出在哪里。
现有代码
登录路由处理 /api/login/route.ts
import { api } from '@/lib/axios'; import { NextRequest, NextResponse } from 'next/server'; import { cookies } from 'next/headers'; import { LoginResponseDto } from '@/types/auth'; import { revalidatePath } from 'next/cache'; export async function POST(request: NextRequest) { try { const data = await request.json(); const res = await api.post<LoginResponseDto>( `${process.env.API_BASE_URL}/login_check`, data ); if (res.status !== 200) { return NextResponse.json( { error: 'Failed to login', }, { status: res.status } ); } const token = res.data.token; const response = NextResponse.json( { success: true, message: 'Login successful' }, { status: res.status } ); response.cookies.set('access_token', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', maxAge: 60 * 60 * 24 * 7, path: '/', }); revalidatePath('/'); return response; } catch (error) { return NextResponse.json( { error: error instanceof Error ? error.message : 'Failed to login', }, { status: 500 } ); } }
动态头部组件
import { checkToken } from '@/app/actions/checkToken/checkToken'; import Header from './Header'; import HeaderDashboard from './HeaderDashboard'; export default async function DynamicHeader() { const token = await checkToken(); return token?.value ? <HeaderDashboard /> : <Header />; }
checkToken服务端动作
'use server' import { cookies } from 'next/headers'; export async function checkToken() { const cookieStore = cookies(); const token = cookieStore.get('access_token'); return token; }
登出服务端动作
'use server'; import { redirect } from 'next/navigation'; import { cookies } from 'next/headers'; import { revalidatePath } from 'next/cache'; export async function logout() { const cookieStore = cookies(); const token = cookieStore.get('access_token'); if (token) { cookieStore.delete('access_token'); revalidatePath('/', 'page'); } redirect('/'); }
首页代码
// import HomeOne from "@/components/homes/home"; import Header from '../components/layout/headers/Header'; // import MobileMenu from "@/components/layout/component/MobileMenu"; import HomeHero from '../components/homes/heros/HomeHero'; import Courses from '../components/homes/courses/Courses'; import FooterOne from '../components/layout/footers/FooterOne'; import Preloader from '@/components/common/Preloader'; import { api } from '@/lib/axios'; import { GenericResponse } from '@/types/response'; import { ICourse } from '@/types/course'; export const dynamic = 'force-dynamic'; // export const fetchCache = 'no-store'; // export const revalidate = 0; export const metadata = { title: 'Home | **', description: '***', }; export default async function HomePage() { const res = await api.get<GenericResponse<ICourse[]>>('/public/home/course'); return ( <> <Preloader /> <Header /> <div className="content-wrapper js-content-wrapper overflow-hidden"> <HomeHero /> <Courses courses={res.data.data} /> <FooterOne /> </div> </> ); }
根布局代码
'use client'; // import "bootstrap/dist/css/bootstrap.min.css"; import '../public/assets/sass/styles.scss'; import { config } from '@fortawesome/fontawesome-svg-core'; import '@fortawesome/fontawesome-svg-core/styles.css'; import 'react-calendar/dist/Calendar.css'; import 'react-toastify/dist/ReactToastify.css'; config.autoAddCss = false; import AOS from 'aos'; import 'aos/dist/aos.css'; import { useEffect } from 'react'; import Context from '@/context/Context'; import { ToastContainer } from 'react-toastify'; export default function RootLayout({ children }) { useEffect(() => { AOS.init({ duration: 700, offset: 120, easing: 'ease-out', once: true, }); }, []); return ( <html lang="en" className=""> <head></head> <body> <ToastContainer /> <Context> {children}</Context> </body> </html> ); }
问题原因分析
核心问题是客户端路由跳转时,Server Components的缓存未即时更新:
- 登录后:前端调用登录接口成功后,仅在服务端设置cookie并调用
revalidatePath('/'),但如果用客户端路由(如useRouter.push)跳转首页,首页的Server Components(含动态头部)仍使用缓存数据,未重新执行checkToken获取最新cookie状态。 - 登出后:服务端动作删除cookie并执行
revalidatePath,但redirect('/')的服务端跳转可能和客户端状态不同步,客户端路由缓存未失效。
解决方案
1. 登录后客户端强制刷新路由
若登录逻辑在客户端组件中,登录成功后调用router.refresh()强制刷新页面,确保Server Components重新获取最新状态:
'use client'; import { useRouter } from 'next/navigation'; async function handleLogin(username: string, password: string) { const res = await fetch('/api/login', { method: 'POST', body: JSON.stringify({ username, password }), headers: { 'Content-Type': 'application/json' } }); if (res.ok) { const router = useRouter(); router.refresh(); // 强制刷新,触发Server Components重新渲染 router.push('/'); } }
2. 首页替换为动态头部组件
检查首页代码,将普通Header替换为DynamicHeader,确保头部能根据cookie状态动态切换:
// 首页代码修改 import DynamicHeader from '../components/layout/headers/DynamicHeader'; // 替换原Header导入 export default async function HomePage() { const res = await api.get<GenericResponse<ICourse[]>>('/public/home/course'); return ( <> <Preloader /> <DynamicHeader /> {/* 使用动态头部 */} <div className="content-wrapper js-content-wrapper overflow-hidden"> <HomeHero /> <Courses courses={res.data.data} /> <FooterOne /> </div> </> ); }
3. 登出后客户端同步刷新
若登出按钮是客户端组件,调用服务端动作后执行router.refresh(),确保客户端状态与服务端同步:
'use client'; import { logout } from '@/app/actions/logout'; import { useRouter } from 'next/navigation'; async function handleLogout() { await logout(); const router = useRouter(); router.refresh(); }
4. 保留首页的动态渲染配置
首页已设置dynamic: 'force-dynamic',确保页面不会被静态缓存,该配置正确,继续保留即可。
总结
通过上述调整,登录/登出操作后头部状态会即时更新,无需手动刷新浏览器。核心是确保客户端路由跳转时,强制刷新Server Components缓存,让checkToken重新执行以获取最新的cookie状态。
内容的提问来源于stack exchange,提问作者Arsany Benyamin
相关产品推荐
相关产品推荐

