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

React导航栏基于Firebase登录状态渲染,避免重复查询Firestore

解决方案

要解决页面切换时重复查询Firestore的问题,核心是仅在用户登录状态变化时触发查询,并缓存已获取的用户信息,避免同一用户重复请求。以下是两种可行方案:

方案一:组件内缓存用户信息

修改Navbar组件的逻辑,利用useRef缓存当前用户邮箱,结合useAuthState的用户状态变化触发查询:

import { useState, useEffect, useRef } from 'react';
import { getAuth, useAuthState } from 'firebase/auth';
import { Link } from 'react-router-dom';
import { getUserDocFromEmail } from './firebaseQuery';
import styles from './Navbar.module.css';

const Navbar = () => {
  const auth = getAuth();
  const [user, authLoading] = useAuthState(auth);
  const [userInfoLoading, setUserInfoLoading] = useState(false);
  const [userInfo, setUserInfo] = useState({});
  // 缓存当前用户邮箱,避免同一用户重复查询
  const currentUserEmailRef = useRef(null);

  async function fetchUserData(userEmail) {
    if (!userEmail) return;
    // 若当前用户已查询过,直接跳过
    if (currentUserEmailRef.current === userEmail) return;
    
    setUserInfoLoading(true);
    try {
      const res = await getUserDocFromEmail(userEmail);
      setUserInfo(res || {});
      currentUserEmailRef.current = userEmail;
    } catch (err) {
      console.error('获取用户信息失败:', err);
      setUserInfo({});
    } finally {
      setUserInfoLoading(false);
    }
  }

  useEffect(() => {
    // 登出时清空缓存和用户信息
    if (!user) {
      setUserInfo({});
      currentUserEmailRef.current = null;
      return;
    }
    // 登录状态变化时查询用户信息
    fetchUserData(user.email);
  }, [user]); // 仅当user状态变化时执行

  return (
    <div className={styles.authBtns}>
      {!authLoading && !user ? (
        <>
          <Link to='/login' className={styles.navbtn}>登录</Link>
          <Link to='/signup' className={styles.navbtn}>注册</Link>
        </>
      ) : (
        <Link to='/profile' className={styles.welcomeLink}>
          {!authLoading && user && !userInfoLoading && userInfo.Username
            ? userInfo.Username
            : 'Loading...'}
        </Link>
      )}
    </div>
  );
};

export default Navbar;

方案二:全局Context管理用户状态(推荐)

对于多组件需要用户信息的场景,使用React Context全局缓存用户数据,避免重复查询:

1. 创建UserContext

import { createContext, useContext, useEffect, useState, useRef } from 'react';
import { getAuth, onAuthStateChanged } from 'firebase/auth';
import { getUserDocFromEmail } from './firebaseQuery';

const UserContext = createContext();

export function UserProvider({ children }) {
  const auth = getAuth();
  const [user, setUser] = useState(null);
  const [userInfo, setUserInfo] = useState({});
  const [isLoading, setIsLoading] = useState(true);
  const currentUserEmailRef = useRef(null);

  async function fetchUserData(userEmail) {
    if (!userEmail || currentUserEmailRef.current === userEmail) return;
    
    try {
      const res = await getUserDocFromEmail(userEmail);
      setUserInfo(res || {});
      currentUserEmailRef.current = userEmail;
    } catch (err) {
      console.error('获取用户信息失败:', err);
      setUserInfo({});
    }
  }

  useEffect(() => {
    // 监听用户登录状态变化
    const unsubscribe = onAuthStateChanged(auth, (authUser) => {
      setIsLoading(true);
      if (authUser) {
        setUser(authUser);
        fetchUserData(authUser.email);
      } else {
        setUser(null);
        setUserInfo({});
        currentUserEmailRef.current = null;
      }
      setIsLoading(false);
    });

    return unsubscribe; // 组件卸载时清理监听
  }, []);

  return (
    <UserContext.Provider value={{ user, userInfo, isLoading }}>
      {children}
    </UserContext.Provider>
  );
}

export function useUser() {
  return useContext(UserContext);
}

2. 在Navbar中使用Context

import { useUser } from './UserContext';
import { Link } from 'react-router-dom';
import styles from './Navbar.module.css';

const Navbar = () => {
  const { user, userInfo, isLoading } = useUser();

  return (
    <div className={styles.authBtns}>
      {!isLoading && !user ? (
        <>
          <Link to='/login' className={styles.navbtn}>登录</Link>
          <Link to='/signup' className={styles.navbtn}>注册</Link>
        </>
      ) : (
        <Link to='/profile' className={styles.welcomeLink}>
          {!isLoading && user && userInfo.Username
            ? userInfo.Username
            : 'Loading...'}
        </Link>
      )}
    </div>
  );
};

export default Navbar;

关键优化点

  • 触发时机:仅在用户登录/登出状态变化时查询Firestore,页面切换时若用户状态未变则不重复请求。
  • 缓存机制:用useRef记录已查询的用户邮箱,避免同一用户重复查询。
  • 全局共享:Context方案让所有组件共享用户数据,无需各组件单独查询,提升性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 09:41:00