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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 05:04:52