使用Next JS 14+Firebase登录后出现反复重定向问题求助
登录后页面反复重定向的原因及解决方案
问题背景
用Next JS 14、shadcn/ui和Firebase开发应用,实现了通过signInWithPopup完成第三方登录后跳转至/home路由的功能。同时做了两个路由守卫:
- 根页面(
src/app/page.tsx):若用户已登录,重定向到/home - 首页(
src/app/home/page.tsx):若用户未登录,重定向回根目录
但登录后出现页面反复在根目录和/home之间跳转的问题。
用户代码如下:
src/app/page.tsx
"use client"; import { useEffect } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { FaGoogle, FaGithub, FaFacebook } from "react-icons/fa"; import { app } from "@/lib/firebase"; import { getAuth, signInWithPopup, GoogleAuthProvider, GithubAuthProvider, FacebookAuthProvider } from "firebase/auth"; import { useAuthState } from "react-firebase-hooks/auth"; import { useRouter } from "next/navigation"; export default function Home() { const auth = getAuth(app); const router = useRouter(); const [user, loading] = useAuthState(auth); useEffect(() => { if (user) { window.location.href = "/home"; } }, [user]); const withGoogle = () => { const provider = new GoogleAuthProvider(); signInWithPopup(auth, provider) .then(() => { router.push("/home"); }) .catch((err) => { console.error("An error occured: " + err); }); } const withGithub = () => { const provider = new GithubAuthProvider(); signInWithPopup(auth, provider) .then(() => { router.push("/home"); }) .catch((err) => { console.error("An error occured: " + err); }); } const withFacebook = () => { const provider = new FacebookAuthProvider(); signInWithPopup(auth, provider) .then(() => { router.push("/home"); }) .catch((err) => { console.error("An error occured: " + err); }); } return ( <main> {loading || user ? ( <p> Loading... </p> ) : ( <div className="flex justify-center pt-64"> <Card className="w-96"> <CardHeader> <CardTitle> Sign In </CardTitle> <CardDescription> Sign in using one of these methods </CardDescription> </CardHeader> <CardContent> <div> <Button onClick={withGoogle}> <FaGoogle className="mr-2 h-4 w-4" /> Sign In with Google </Button> </div> <div className="pt-3"> <Button onClick={withGithub}> <FaGithub className="mr-2 h-4 w-4" /> Sign In with Github </Button> </div> <div className="pt-3"> <Button onClick={withFacebook}> <FaFacebook className="mr-2 h-4 w-4" /> Sign In with Facebook </Button> </div> </CardContent> </Card> </div> )} </main> ) }
src/app/home/page.tsx
"use client"; import { app } from "@/lib/firebase"; import { getAuth } from "firebase/auth"; import { useAuthState } from "react-firebase-hooks/auth"; import { useEffect } from "react"; function Home() { const auth = getAuth(app); const [user, loading] = useAuthState(auth); useEffect(() => { if (!user) { window.location.href = "/"; } }, [user]); return ( <div> {loading || !user ? ( <p> Loading... </p> ) : ( <p> Hi Mom </p> )} </div> ) } export default Home;
核心原因
未等待Auth状态初始化完成
useAuthState在组件挂载初期会处于loading状态,此时user为undefined。登录后跳转到/home时,Firebase还没完成用户状态同步,/home页面会因!user触发重定向回根目录;而根页面此时已同步到用户状态,又重定向回/home,形成循环。混合使用两种导航方式
window.location.href会触发页面全量刷新,刷新后Firebase Auth需要重新初始化状态,再次进入loading阶段,加剧了状态波动;而router.push是Next.js客户端导航,不会刷新页面,状态保持更稳定。重定向逻辑未排除加载状态
两个页面的重定向逻辑都没有判断loading是否结束,导致在Auth状态未稳定时就触发跳转。
解决方案
1. 重定向前先判断加载状态
确保只有当Auth状态完全初始化(loading为false)时,才根据user状态执行重定向。
2. 统一使用Next.js客户端导航
替换所有window.location.href为router.push,避免页面刷新导致的状态重置。
修改后的代码
src/app/page.tsx
"use client"; import { useEffect } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { FaGoogle, FaGithub, FaFacebook } from "react-icons/fa"; import { app } from "@/lib/firebase"; import { getAuth, signInWithPopup, GoogleAuthProvider, GithubAuthProvider, FacebookAuthProvider } from "firebase/auth"; import { useAuthState } from "react-firebase-hooks/auth"; import { useRouter } from "next/navigation"; export default function LoginPage() { const auth = getAuth(app); const router = useRouter(); const [user, loading] = useAuthState(auth); useEffect(() => { // 等待加载完成后再判断用户状态 if (!loading && user) { router.push("/home"); } }, [user, loading, router]); const withGoogle = () => { const provider = new GoogleAuthProvider(); signInWithPopup(auth, provider) .then(() => router.push("/home")) .catch((err) => console.error("登录错误:", err)); }; const withGithub = () => { const provider = new GithubAuthProvider(); signInWithPopup(auth, provider) .then(() => router.push("/home")) .catch((err) => console.error("登录错误:", err)); }; const withFacebook = () => { const provider = new FacebookAuthProvider(); signInWithPopup(auth, provider) .then(() => router.push("/home")) .catch((err) => console.error("登录错误:", err)); }; if (loading) { return <p>Loading...</p>; } if (user) { return null; // 重定向过程中避免内容闪烁 } return ( <main> <div className="flex justify-center pt-64"> <Card className="w-96"> <CardHeader> <CardTitle>Sign In</CardTitle> <CardDescription>Sign in using one of these methods</CardDescription> </CardHeader> <CardContent> <div> <Button onClick={withGoogle}> <FaGoogle className="mr-2 h-4 w-4" /> Sign In with Google </Button> </div> <div className="pt-3"> <Button onClick={withGithub}> <FaGithub className="mr-2 h-4 w-4" /> Sign In with Github </Button> </div> <div className="pt-3"> <Button onClick={withFacebook}> <FaFacebook className="mr-2 h-4 w-4" /> Sign In with Facebook </Button> </div> </CardContent> </Card> </div> </main> ); }
src/app/home/page.tsx
"use client"; import { app } from "@/lib/firebase"; import { getAuth } from "firebase/auth"; import { useAuthState } from "react-firebase-hooks/auth"; import { useEffect } from "react"; import { useRouter } from "next/navigation"; function Home() { const auth = getAuth(app); const router = useRouter(); const [user, loading] = useAuthState(auth); useEffect(() => { // 等待加载完成后再判断用户状态 if (!loading && !user) { router.push("/"); } }, [user, loading, router]); if (loading || !user) { return <p>Loading...</p>; } return <p>Hi Mom</p>; } export default Home;
额外优化建议
- 把Auth状态判断逻辑封装成自定义Hook(比如
useAuthRedirect),减少重复代码。 - 考虑使用Next.js的Middleware做路由级校验,在页面渲染前就完成Auth状态判断,彻底避免客户端重定向的闪烁和循环问题。
内容的提问来源于stack exchange,提问作者Nathan
相关产品推荐
相关产品推荐

