【问题标题】:Implement user present using Firestore db with Firebase Realtime db in React在 React 中使用 Firestore db 和 Firebase Realtime db 实现用户呈现
【发布时间】:2022-01-03 05:19:48
【问题描述】:

我正在尝试使用 Firebase 在我的 React 应用中设置用户状态。 每次用户登录应用程序时,我都会将其保存为 users 集合,并在我的应用程序中显示为列表。

我需要显示每个用户的在线状态,所以我使用 Firebase Realtime db 来更新 Firestore db,这是整个 AuthContext.js 文件:

import React, { useState, useEffect, useContext, createContext } from "react";
import { db, rtdb } from "../firebase/firebase-config";
import {
  getAuth,
  signInWithPopup,
  GoogleAuthProvider,
  onAuthStateChanged,
  signOut,
} from "firebase/auth";
import {
  addDoc,
  collection,
  serverTimestamp,
  getDocs,
  query,
  where,
  limit,
  updateDoc,
  doc,
} from "firebase/firestore";
import { Redirect } from "react-router-dom";
import { ref as rtdbRef, set, onValue } from "firebase/database";

const authContext = createContext();

export function ProvideAuth({ children }) {
  const auth = useProvideAuth();
  return <authContext.Provider value={auth}>{children}</authContext.Provider>;
}

export const useAuth = () => {
  return useContext(authContext);
};

function useProvideAuth() {
  const [user, setUser] = useState(null);
  const provider = new GoogleAuthProvider();
  const auth = getAuth();
  const [userLoading, setUserLoading] = useState(true);

  const getUserByUid = async (uid) => {
    let exists = false;
    const querySnapshot = await getDocs(
      query(collection(db, "users"), where("uid", "==", uid), limit(1))
    );
    querySnapshot.forEach(() => (exists = true));
    return exists;
  };

  const persistUser = async (user) => {
    const exists = await getUserByUid(user.uid);
    if (!exists) {
      await addDoc(collection(db, "users"), {
        uid: user.uid,
        displayName: user.displayName,
        email: user.email,
        photoURL: user.photoURL,
        createdAt: serverTimestamp(),
        updatedAt: null,
      });
    }
  };

  const logIn = () =>
    signInWithPopup(auth, provider)
      .then((result) => {
        setUser(result.user);
        persistUser(result.user);
      })
      .catch((err) => {
        console.log(err);
      });

  const updateFirestoreUser = (user, status) => {
    if (user) {
      getDocs(
        query(collection(db, "users"), where("uid", "==", user.uid), limit(1))
      ).then((querySnapshot) => {
        querySnapshot.forEach((u) => {
          updateDoc(doc(db, "users", u.id), {
            online: status,
            updatedAt: serverTimestamp(),
          });
        });
      });
    }
  };

  const logOut = () =>
    signOut(auth)
      .then(() => {
        console.log("SIGNOUT", user);
        updateFirestoreUser(user, false);
        setUser(false);
        return <Redirect to="/login" />;
      })
      .catch((err) => {
        console.log(err);
      });

  function checkAuthStatus() {
    return new Promise((resolve, reject) => {
      try {
        onAuthStateChanged(auth, async (user) => {
          resolve(user);
          setUser(user);

          const connectedRef = rtdbRef(rtdb, ".info/connected");
          const myConnectionsRef = rtdbRef(rtdb, `status`);
          onValue(connectedRef, (snap) => {
            if (snap.val() === true) {
              console.log("TRUE");
              if (user) {
                set(myConnectionsRef, "connected");
                updateFirestoreUser(user, true);
              }
            } else {
              console.log("FALSE");
              updateFirestoreUser(user, false);
            }
            //onDisconnect(myConnectionsRef)
              //.set("disconnected")
              //.then(() => {
              //   updateFirestoreUser(user, false);
              //});
          });
        });
      } catch {
        reject("api failed");
        setUser(false);
      }
    });
  }

  const getUser = async () => {
    setUserLoading(true);
    await checkAuthStatus();
    setUserLoading(false);
  };

  useEffect(() => {
    getUser();
  }, []);

  return {
    user,
    logIn,
    logOut,
    userLoading,
  };
}

users 集合中的 online 字段在我登录和注销时会更新,但在我关闭浏览器选项卡时不会更新,当我取消注释这部分时它有点工作:

onDisconnect(myConnectionsRef)
 .set("disconnected")
 .then(() => {
    updateFirestoreUser(user, false);
});

但更改仅反映在 Realtime db 中,Firestore db users 集合中没有更改,字符串“disconnected”已添加到 Realtime 数据库中,但用户条目中没有任何更改我要在 @ 中更新987654329@方法。

在这里卡了2天,我不知道自己做错了什么,我只需要打开应用程序并登录用户时online字段为true,以及@987654332 @当用户退出或在浏览器中关闭应用时。

【问题讨论】:

  • 您是否实现了 Cloud Functions,如下所示? firebase.google.com/docs/firestore/solutions/…
  • @FrankvanPuffelen 我没有找到关于它的 v9 文档,所以我无法按照您提供的链接执行此操作,而是按照此处的文档进行操作:firebase.google.com/docs/database/web/…
  • 该链接显示了如何将存在信息获取到实时数据库中。如果没有我链接的 Cloud Functions 实现,就无法将状态信息从 RTDB 获取到 Firestore。

标签: reactjs firebase firebase-realtime-database google-cloud-firestore


【解决方案1】:

按照您链接的文档中的presence system,您将在实时数据库中获得在线信息。

要将这些信息导入 Firestore,您需要实现一个云函数,该函数会在实时数据库写入中触发,如 implementing presence on Firestore 文档中的 updating presence information in Firestore 部分所示。这是在 Firestore 中获取状态信息的唯一方法,因为该数据库不具备构建状态系统所需的任何类似 onDisconnect 的功能。

【讨论】:

  • 我明白了,非常感谢您的回复。是否有 v9 实现?我在我的项目中使用 Firebase v9。
  • 此代码在 Cloud Functions 上运行,您将在其中使用 Admin SDK 而不是客户端 JavaScript SDK。虽然有一个模块化的 Admin SDK,但我认为它还没有被宣布为稳定的。
  • 好的,我明白了,感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-18
  • 2021-02-11
  • 2019-05-10
  • 1970-01-01
  • 2016-12-04
  • 1970-01-01
  • 2023-04-08
相关资源
最近更新 更多