React+Firebase实现邮箱验证后访问的受保护路由问题
解决React+Firebase受保护路由的邮箱验证重定向问题
问题根源
你的Auth Context中user初始化为空对象{},而空对象在JavaScript中属于真值(truthy),导致Protected Route首次渲染时:
!user条件不成立(空对象非假),不会跳转到登录页;- 进入
user && !user.emailVerified判断,空对象没有emailVerified属性,!undefined为true,因此错误重定向到/email。
同时缺少身份验证的加载状态,无法区分“正在获取用户信息”和“已确认无用户”两种状态,导致初始渲染就执行重定向逻辑。
解决方案
1. 修复Auth Context的状态管理
修改AuthContext.js,将user初始值改为null,并添加isLoading状态跟踪身份验证进度:
import React, { createContext, useEffect, useState } from "react"; import { createUserWithEmailAndPassword, onAuthStateChanged, sendEmailVerification, signInWithEmailAndPassword, signOut, onIdTokenChanged } from 'firebase/auth'; import { auth } from '../../firebase-config'; import { createUserDocument } from "../../utils/userUtils"; const UserContext = createContext(); export const AuthContextProvider = ({ children }) => { // 初始user设为null,区分未登录和加载中 const [user, setUser] = useState(null); // 添加加载状态,初始为true const [isLoading, setIsLoading] = useState(true); /** Create a new user */ const createUser = async (email, password, role) => { try { await createUserWithEmailAndPassword(auth, email, password).then((userCredential) => { const currentUser = userCredential.user const userProps = { id: currentUser.uid, email: currentUser.email, role: role } createUserDocument(userProps) sendVerificationEmail(currentUser); }); } catch (error) { console.error(error); } }; /** Sends authenticated user a verification email */ const sendVerificationEmail = (currentUser=user) => { const actionCodeSettings = { url: window.location.origin + '/home', handleCodeInApp: true, }; sendEmailVerification(currentUser, actionCodeSettings).then(() => { console.log(`Verification email sent to user ${currentUser.email}`); }) } /** Login a user into the tool */ const login = async (email, password) => { const user = await signInWithEmailAndPassword(auth, email, password); return user }; /** Logout a user from the tool */ const logout = () => { return signOut(auth); }; useEffect(() => { const authState = onAuthStateChanged(auth, (currentUser) => { setUser(currentUser); // 获取到用户状态后,标记加载完成 setIsLoading(false); }); const tokenState = onIdTokenChanged(auth, (currentUser) => { // console.log("User Token Changed", currentUser) }) return () => { authState(); tokenState(); }; }, []); const contextValues = { createUser, sendVerificationEmail, login, logout, user, // 将isLoading加入上下文 isLoading }; return ( <UserContext.Provider value={contextValues}> {children} </UserContext.Provider> ); }; export const UserAuth = UserContext;
2. 优化Protected Route的判断逻辑
修改ProtectedRoute.js,先判断加载状态,再依次验证用户登录状态和邮箱验证状态:
import React, { useContext } from 'react' import { UserAuth } from '../Context/AuthContext' import { Navigate } from 'react-router-dom' const ProtectedRoute = ({children}) => { const { user, isLoading } = useContext(UserAuth); // 加载中时,显示加载提示(可替换为Spinner组件) if (isLoading) { return <div>加载中...</div>; } // 未登录用户,跳转到登录页 if (!user) { return <Navigate to='/' /> } // 已登录但未验证邮箱,跳转到邮箱验证页 if (!user.emailVerified) { return <Navigate to='/email'/> } // 验证通过,放行访问受保护页面 return children } export default ProtectedRoute
关键说明
- 初始
user设为null:Firebase的onAuthStateChanged在用户未登录时会传入null,与初始状态一致,确保未登录判断准确; isLoading状态:避免在Firebase还未返回用户信息时就执行重定向逻辑,提升用户体验;- 分层判断逻辑:依次处理加载中、未登录、未验证邮箱三种情况,逻辑清晰且避免错误跳转。
内容的提问来源于stack exchange,提问作者xw-liron
相关产品推荐
相关产品推荐

