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

Next.js中next-auth已登录用户访问/login路由重定向报错排查

Next.js + Next-Auth 登录重定向报错问题排查与修复

问题背景

在自研Web应用中使用Next.js的Next-Auth实现身份验证,需求是已登录用户访问/login路由时自动重定向至首页,但运行时出现错误:Cannot update a component (Router) while rendering a different component (Login)。该方案在Pages Router模式下曾正常运行,当前使用App Router模式出现异常。相关代码如下:

原Login组件代码

'use client'

import { signIn, useSession } from 'next-auth/react';
import { useRouter } from "next/navigation";

function Login() {
    const sessionData = useSession();
    const router = useRouter();
    console.log('sessionData:', sessionData)
    if (sessionData.status == "authenticated") {
        router.push("/pages/list-clients");
    } else {
        if (sessionData.status === "loading") {
            return <div>Loading...</div>
        } else {
            signIn("keycloak")
                .catch(error => {
                    console.error("Error occurred during sign in KeyCloak:", error);
                });
        }
    }
}

export default Login

关联组件代码

layout.tsx

export default function RootLayout({
  children,
  component: Component
}: Readonly<{
  children: React.ReactNode;
  component: any
}>) {
  const requiresAuth = Component?.requiresAuth || true;

  return (
    <html lang="en">
      <body className={`${geistSans.variable} ${geistMono.variable}`}>
        <Providers>
          {requiresAuth ? 
              <Auth>
                <TopNavbar />
                {children}
                <Footer />
              </Auth>
            : children
          }
        </Providers> 
      </body>
    </html>
  );
}

Auth.tsx

const Auth = ({ children }: { children: React.ReactNode }) => {
  const { data: session, status } = useSession();
  const router = useRouter();

  useEffect(() => {
      if (status === 'unauthenticated') {
          signIn("keycloak")
              .catch(error => {
                  console.error("Error occurred during sign in KeyCloak:", error);
              });
      }
  }, [status, router]);

  if (status === 'loading') {
      return <div>Loading...</div>;
  }

  if (!session) {
      return <div>Redirecting...</div>;
  }

  return <> {children} </>;
};

export default Auth;

Providers.tsx

"use client";
import {SessionProvider} from "next-auth/react";

export function Providers({children}: {children: React.ReactNode}) {
  return <SessionProvider>{children}</SessionProvider>;
}

错误原因

  1. 渲染阶段触发副作用:在Login组件的渲染流程中直接调用router.push和signIn,这两个操作都会触发路由或登录状态的更新。React的App Router模式对渲染阶段的副作用有严格限制,不允许在渲染一个组件时更新另一个组件(如Router)的状态。
  2. 组件未返回有效JSX:Login组件在authenticated和unauthenticated状态下都没有返回合法的JSX元素(仅loading状态有返回),违反React组件必须返回JSX的规则,导致渲染异常。
  3. 鉴权逻辑重复冲突:RootLayout默认对所有页面启用鉴权(requiresAuth = true),Login页面被Auth组件包裹后,Auth组件的未登录跳转逻辑与Login组件的登录触发逻辑重复,加剧了状态更新冲突。

修复方案

1. 重构Login组件,用useEffect处理副作用

将路由跳转和登录触发逻辑移至useEffect钩子中,确保在组件挂载或状态变化时执行,而非渲染阶段。同时保证组件始终返回有效JSX:

'use client'

import { signIn, useSession } from 'next-auth/react';
import { useRouter } from "next/navigation";
import { useEffect } from 'react';

function Login() {
    const { status } = useSession();
    const router = useRouter();

    useEffect(() => {
        // 已登录状态下跳转至目标页面
        if (status === "authenticated") {
            router.push("/pages/list-clients");
        } 
        // 未登录状态下触发登录流程
        else if (status === "unauthenticated") {
            signIn("keycloak")
                .catch(error => {
                    console.error("Error occurred during sign in KeyCloak:", error);
                });
        }
    }, [status, router]);

    // 加载状态显示提示
    if (status === "loading") {
        return <div>Loading...</div>;
    }

    // 未登录状态下返回跳转提示(signIn会自动处理跳转)
    return <div>Redirecting to login...</div>;
}

// 标记当前页面不需要鉴权,避免被Auth组件包裹
Login.requiresAuth = false;

export default Login;

2. 确保Login页面跳过鉴权

通过给Login组件添加requiresAuth = false属性,让RootLayout不对该页面启用Auth组件包裹,避免鉴权逻辑重复。

3. 验证Auth组件逻辑

Auth组件的现有逻辑(通过useEffect处理未登录跳转)是合理的,仅需确保它只作用于需要鉴权的页面即可,无需额外修改。

模式差异说明

Pages Router与App Router的渲染机制不同:Pages Router对渲染阶段的副作用限制较宽松,而App Router的客户端组件严格遵循React的渲染规则,禁止在渲染过程中触发状态更新或路由跳转,必须通过useEffect等钩子在渲染完成后执行副作用操作,这也是之前方案可行现在报错的核心原因。

内容的提问来源于stack exchange,提问作者tad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 09:05:06