You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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是强制页面刷新,会触发服务端的登出逻辑并彻底重置客户端状态,所以一次点击就生效。

你可以直接将<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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 19:17:36