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

Next 13 App Router+Typescript+Firebase路由保护问题求助

Next 13 App Router + Firebase 路由保护问题解决方案

问题根源分析

  1. 布局层级错误:原代码中ProtectedRoutes包裹了AuthProvider,导致组件渲染时useAuth无法立即获取认证状态,isLoading初始状态未正确拦截,造成未登录用户短暂看到首页后才跳转。
  2. 路由规则不完整:仅处理了未登录用户的跳转逻辑,未限制已登录用户访问/login路由。
  3. 无加载占位:认证状态加载过程中直接渲染页面内容,引发闪屏。

解决方案步骤

1. 调整布局层级,确保Auth上下文优先加载

修改src/app/layout.tsx,将AuthProvider放在最外层,保证ProtectedRoutes能获取完整的认证状态:

import { AuthProvider } from '@/hooks/useAuth';
import '../styles/globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import ProtectedRoutes from '@/components/ProtectedRoutes';

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Proposals Mayara Dev',
  description: 'Proposals System Mayara Becker Dev',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body suppressHydrationWarning={true} className={inter.className}>
        <AuthProvider>
          <ProtectedRoutes>
            {children}
            <ToastContainer position='bottom-right' />
          </ProtectedRoutes>
        </AuthProvider>
      </body>
    </html>
  )
}

2. 重构ProtectedRoutes组件,完善路由规则

更新ProtectedRoutes.tsx,增加路由判断逻辑,同时在认证加载过程中显示占位组件(避免闪屏):

"use client"

import { useEffect } from 'react';
import { useRouter, usePathname } from 'next/navigation';
import { useAuth } from '@/hooks/useAuth';

// 可自定义加载组件,比如Spinner
const Loading = () => <div>Loading...</div>;

const ProtectedRoutes = ({ children }: { children: React.ReactNode }) => {
  const { isLoggedIn, isLoading } = useAuth();
  const router = useRouter();
  const pathname = usePathname();

  useEffect(() => {
    if (isLoading) return;

    // 已登录用户访问/login,跳转到首页
    if (isLoggedIn && pathname === '/login') {
      router.push('/');
      return;
    }

    // 未登录用户访问非/login路由,跳转到/login
    if (!isLoggedIn && pathname !== '/login') {
      router.push('/login');
    }
  }, [isLoggedIn, isLoading, router, pathname]);

  // 加载过程中显示占位,阻止渲染页面内容
  if (isLoading) {
    return <Loading />;
  }

  // 已登录或当前是/login页面(未登录状态),渲染内容
  return <>{children}</>;
};

export default ProtectedRoutes;

3. 确保useAuth Hook的状态初始化正确

检查useAuth Hook,确保isLoading初始值为true,直到Firebase Auth完成初始化:

// 示例useAuth Hook核心逻辑
"use client"

import { useState, useEffect } from 'react';
import { getAuth, onAuthStateChanged, User } from 'firebase/auth';

export const useAuth = () => {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true); // 初始为true

  useEffect(() => {
    const auth = getAuth();
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setUser(user);
      setIsLoading(false); // 初始化完成后设为false
    });

    return unsubscribe;
  }, []);

  return {
    isLoggedIn: !!user,
    user,
    isLoading
  };
};

关键注意事项

  • 避免重复跳转:通过pathname判断当前路由,防止无限循环跳转。
  • 加载占位:认证状态未确定时,必须阻止页面内容渲染,彻底解决闪屏问题。
  • Firebase Auth初始化:确保onAuthStateChanged正确监听状态,且isLoading在初始化完成后才切换为false。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 12:10:31