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

React+FirebaseStore使用signInWithRedirect无法创建docSnap问题

问题原因及解决方案

你遇到的核心问题是**signInWithRedirect的工作机制和signInWithPopup完全不同**:

调用signInWithRedirect后,浏览器会跳转到谷歌的授权页面,授权完成后再跳转回你的应用。这意味着你写在signInWithRedirect之后的所有代码(比如console.log(resultRef.user)、检查数据库、跳转页面)根本不会执行——因为原页面已经被销毁了,跳转回来后是重新加载的新页面。

而signInWithPopup是在当前页面弹出授权窗口,页面不会刷新,所以后续代码能正常运行。


修正步骤

  1. 简化移动端登录函数:只负责触发重定向,不做后续处理
  2. 在页面加载时处理重定向结果:用getRedirectResult获取登录信息,再执行数据库检查和跳转逻辑
  3. 修复useEffect的无限循环问题:添加正确的依赖项

修改后的代码示例

1. 调整onGoogleSignIn函数

async function onGoogleSignIn() {
  try {
    const auth = getAuth();
    const provider = new GoogleAuthProvider();
    if (isMobile) {
      // 移动端只触发重定向,后续逻辑交给useEffect处理
      await signInWithRedirect(auth, provider);
    } else {
      // 桌面端popup逻辑保持不变
      const resultRef = await signInWithPopup(auth, provider);
      const user = resultRef.user;
      const data = {
        uid: user.uid,
        email: user.email,
        verified: false,
        name: user.displayName,
      };
      userData(data);

      const docRef = doc(db, "users", user.uid);
      const docSnap = await getDoc(docRef);
      if (!docSnap.exists()) {
        await setDoc(docRef, {
          displayName: user.displayName,
          email: user.email,
          uid: user.uid,
          timestamp: serverTimestamp(),
        });
        navigate("/additional-info");
      } else {
        // 已有用户文档,根据验证状态跳转
        const userDoc = docSnap.data();
        navigate(userDoc.verified ? "/" : "/additional-info");
      }
    }
  } catch (error) {
    toast.error("Could not sign in with google");
    console.log(error);
  }
}

2. 重构useEffect处理重定向和状态监听

useEffect(() => {
  const auth = getAuth();

  // 处理重定向登录的结果
  const handleRedirectLogin = async () => {
    try {
      const result = await getRedirectResult(auth);
      if (result) {
        const user = result.user;
        const data = {
          uid: user.uid,
          email: user.email,
          verified: false,
          name: user.displayName,
        };
        userData(data);

        // 检查数据库是否存在用户文档
        const docRef = doc(db, "users", user.uid);
        const docSnap = await getDoc(docRef);
        if (!docSnap.exists()) {
          // 创建新文档并跳转到补充信息页
          await setDoc(docRef, {
            displayName: user.displayName,
            email: user.email,
            uid: user.uid,
            timestamp: serverTimestamp(),
          });
          navigate("/additional-info");
        } else {
          // 已有文档,根据验证状态跳转
          const userDoc = docSnap.data();
          navigate(userDoc.verified ? "/" : "/additional-info");
        }
      }
    } catch (error) {
      console.error("重定向登录处理失败:", error);
      toast.error("登录失败,请重试");
    }
  };

  // 监听用户状态变化(处理页面刷新后的持久化登录)
  const unsubscribe = onAuthStateChanged(auth, async (user) => {
    if (user) {
      const data = {
        uid: user.uid,
        email: user.email,
        verified: false,
        name: user.displayName,
      };
      userData(data);

      // 检查用户文档状态
      const docRef = doc(db, "users", user.uid);
      const docSnap = await getDoc(docRef);
      if (docSnap.exists()) {
        const userDoc = docSnap.data();
        navigate(userDoc.verified ? "/" : "/additional-info");
      }
    }
  });

  // 页面加载时先处理重定向结果
  handleRedirectLogin();

  // 组件卸载时取消监听
  return () => unsubscribe();
}, [navigate, userData]); // 添加依赖项,避免无限循环

额外注意点

  • 原代码中useEffect没有依赖项,会导致组件每次渲染都重新执行,引发无限循环,必须加上navigate和userData作为依赖
  • 不要在signInWithRedirect之后写任何逻辑,因为这些代码永远不会被执行
  • 重定向后的页面加载时,getRedirectResult会返回登录结果,如果用户已经登录过(比如刷新页面),则onAuthStateChanged会触发,处理持久化登录状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 02:35:02