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

onSnapshot在onAuthStateChanged中返回错误用户数据问题排查

问题

在React中使用Firebase时,切换账号后更新当前用户(比如添加新关注),onSnapshot会返回错误的用户数据(显示我关注的用户,而非更新后的当前用户),刷新页面后数据才恢复正常。控制台打印的user.uid与doc.data().uid一致,但React控制台显示的用户数据却不符,getAuth()能保持正确的登录用户,推测是缓存或订阅清理问题。

监听用户更新的函数

useEffect(() => {
  let unsubscribe;
  onAuthStateChanged(auth, (user) => {
    if (user) {
      const docRef = doc(db, "users", user.uid);
      unsubscribe = onSnapshot(docRef, (doc) => {
        console.log(user.uid);
        console.log(doc.data().uid);
        setUserState(doc.data());
      });
    } else {
      setUserState(false);
    }
  });
  return () => {
    if (unsubscribe) {
      unsubscribe();
    }
  };
}, []);

更新关注关系的函数

async function updateFollow(db, follower, followee){
  const followerRef = doc(db, "users", follower.uid);
  const followerSnap = await getDoc(followerRef);
  const followerData = followerSnap.data();

  const followeeRef = doc(db, "users", followee.uid);

  if (followerData.following && followerData.following.includes(followee.profileName)){
    await updateDoc(followerRef, {
      following: arrayRemove(followee.profileName)
    })
    await updateDoc(followeeRef, {
      followers: arrayRemove(follower.profileName)
    })
  } else{
    await updateDoc(followerRef, {
      following: arrayUnion(followee.profileName)
    })
    await updateDoc(followeeRef, {
      followers: arrayUnion(follower.profileName)
    })
  }
}

问题原因与解决方法

核心问题:订阅未正确清理

切换账号时,旧的onSnapshot订阅未及时取消,导致新、旧订阅同时触发,旧订阅的回调覆盖了新的用户状态。原代码的清理逻辑存在漏洞:onAuthStateChanged是异步触发的,账号切换时旧的订阅可能还未被清理,新订阅就已创建。

修复步骤

  1. 彻底清理所有订阅
    修改useEffect逻辑,在每次触发onAuthStateChanged回调时,先取消旧的快照订阅,同时新增onAuthStateChanged自身的清理:

    useEffect(() => {
      let unsubscribeSnapshot;
      // 保存auth监听的取消函数
      const unsubscribeAuth = onAuthStateChanged(auth, (user) => {
        // 先取消旧的快照订阅
        if (unsubscribeSnapshot) {
          unsubscribeSnapshot();
        }
        if (user) {
          const docRef = doc(db, "users", user.uid);
          unsubscribeSnapshot = onSnapshot(docRef, (doc) => {
            console.log(user.uid);
            console.log(doc.data().uid);
            setUserState(doc.data());
          });
        } else {
          setUserState(false);
        }
      });
      // 组件卸载时同时清理auth监听和快照订阅
      return () => {
        unsubscribeAuth();
        if (unsubscribeSnapshot) {
          unsubscribeSnapshot();
        }
      };
    }, []);
    
  2. 规避闭包陷阱
    原代码中onSnapshot回调引用的user可能因闭包保留旧值,可直接使用auth.currentUser.uid创建文档引用,确保每次获取最新的用户ID:

    const docRef = doc(db, "users", auth.currentUser.uid);
    
  3. 强制从服务器获取数据(可选)
    若怀疑是Firestore缓存导致的问题,可在onSnapshot中指定优先从服务器拉取数据:

    unsubscribeSnapshot = onSnapshot(docRef, { source: 'server' }, (doc) => {
      setUserState(doc.data());
    });
    
  4. 校验更新函数参数
    调用updateFollow时,确保传入的follower是当前登录用户的最新数据,避免因传入旧用户对象导致更新错误文档。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 17:32:28