我正在尝试使用 Firebase 在我的 React 应用程序中设置用户状态。 每次用户登录应用程序时,我都会将其保存为
users
集合,并将其显示为我的应用程序中的列表。
我需要显示每个用户的在线状态,因此我使用 Firebase Realtime 数据库来更新 Firestore 数据库,这是整个 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,
};
}
当我登录和注销时,
online
集合中的users
字段会更新,但当我关闭浏览器选项卡时不会更新,当我取消注释这部分时它会起作用:
onDisconnect(myConnectionsRef)
.set("disconnected")
.then(() => {
updateFirestoreUser(user, false);
});
但是这些更改仅反映在实时数据库中,Firestore 数据库
users
集合中没有任何更改,字符串“disconnected”被添加到实时数据库中,但我要在then()
中更新的用户条目中没有任何更改方法。
被困在这里2天了,我不知道我做错了什么,我只需要当应用程序打开并且用户登录时
online
字段为true
,以及当用户登录时false
用户已注销或应用程序在浏览器中已关闭。
通过遵循您链接的文档中的状态系统,您将在实时数据库中获取状态信息。
要将这些信息输入 Firestore,您需要实现一个在实时数据库写入中触发的云函数,如在 Firestore 上实现状态文档中的更新 Firestore 中的状态信息部分所示。这是在 Firestore 中获取状态信息的唯一方法,因为该数据库没有任何构建状态系统所需的
onDisconnect
之类的功能。
我也在开发一个聊天应用程序,并想为其添加状态功能,但我和你一样遇到错误。 你能分享一下 rtdb 的代码吗,因为我收到了此导入中未定义的错误 从“../firebase/firebase-config”导入{ db, rtdb }; 如果您能分享代码,这对我真的很有帮助。