使用useRouter()时按钮需多次点击?Next.js组件问题求助
问题描述
我有两段代码片段,第一段可正常运行,但该组件接收baseUrl参数,我希望组件无需接收任何参数。查阅Next.js文档得知优先推荐使用<Link>组件,若无法使用则用useRouter()。我不清楚如何在按钮内部使用<Link>,因此选择使用useRouter(),但使用该方法时(见第二段代码),按钮需要点击3-5次才会生效,而第一段代码只需点击一次即可。请问我哪里操作出错了?
可正常运行的代码:
"use client"; import { useAuth, useLoginWithRedirect } from "@frontegg/nextjs"; export const ClientComponent = ({ baseUrl }: { baseUrl?: string }) => { const { isAuthenticated } = useAuth(); const loginWithRedirect = useLoginWithRedirect(); const logout = () => { window.location.href = `${baseUrl}/account/logout`; }; return ( <div> {isAuthenticated ? ( <div> <button onClick={() => logout()}>Click to logout</button> </div> ) : ( <div> <button onClick={() => loginWithRedirect()}>Click me to login</button> </div> )} </div> ); };
无法正常运行的代码:
"use client"; import { useAuth, useLoginWithRedirect } from "@frontegg/nextjs"; import { useRouter } from "next/navigation"; export const ClientComponent = () => { const { isAuthenticated } = useAuth(); const loginWithRedirect = useLoginWithRedirect(); const router = useRouter(); return ( <div> {isAuthenticated ? ( <div> <button onClick={() => router.push("/account/logout")}> Click to logout </button> </div> ) : ( <div> <button onClick={() => loginWithRedirect()}>Click me to login</button> </div> )} </div> ); };
解决方案
1. 为什么router.push需要多次点击?
router.push是Next.js的客户端SPA导航方法,它会保留客户端状态并进行无刷新跳转。但登出逻辑需要清除认证状态并触发服务端的登出流程,直接用router.push会出现两个核心问题:
- Frontegg的本地认证状态还没来得及更新,组件重新渲染时
isAuthenticated依然为true,导致按钮没有消失,看起来像没触发跳转 - 客户端导航不会强制刷新页面,服务端的登出Cookie/Token清理可能无法同步到客户端,导致状态残留
而原来的window.location.href是强制页面刷新,会触发服务端的登出逻辑并彻底重置客户端状态,所以一次点击就生效。
2. 正确使用<Link>包裹按钮的方式
你可以直接将<Link>作为按钮的父元素,原生HTML按钮无需额外配置:
"use client"; import { useAuth, useLoginWithRedirect } from "@frontegg/nextjs"; import Link from "next/link"; export const ClientComponent = () => { const { isAuthenticated } = useAuth(); const loginWithRedirect = useLoginWithRedirect(); return ( <div> {isAuthenticated ? ( <div> <Link href="/account/logout"> <button>Click to logout</button> </Link> </div> ) : ( <div> <button onClick={() => loginWithRedirect()}>Click me to login</button> </div> )} </div> ); };
不过这种方式本质和router.push一致,依然可能存在状态不同步的问题,所以更推荐用官方登出方法。
3. 最优解:使用Frontegg官方登出API
Frontegg提供了专门的useLogout钩子,会自动处理认证状态清除、跳转逻辑,完全不需要手动处理baseUrl或导航:
"use client"; import { useAuth, useLoginWithRedirect, useLogout } from "@frontegg/nextjs"; export const ClientComponent = () => { const { isAuthenticated } = useAuth(); const loginWithRedirect = useLoginWithRedirect(); const logout = useLogout(); return ( <div> {isAuthenticated ? ( <div> <button onClick={() => logout()}>Click to logout</button> </div> ) : ( <div> <button onClick={() => loginWithRedirect()}>Click me to login</button> </div> )} </div> ); };
这个方法会同步清理客户端状态并触发服务端登出流程,不会出现多次点击的问题,同时满足你无需传入baseUrl的需求。
总结
- 登出这类需要清除全局状态的操作,优先使用第三方Auth库的官方方法,避免手动导航
- 若必须用导航,
window.location.href在登出场景下比router.push更可靠(会强制刷新) <Link>可直接嵌套按钮,无需特殊配置,但同样存在状态同步问题
内容的提问来源于stack exchange,提问作者Sandra Schlichting
相关产品推荐
相关产品推荐

