在 React 中使用 Firestore db 和 Firebase Realtime db 实现用户呈现
Implement user present using Firestore db with Firebase Realtime db in React
我正在尝试使用 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);
});
但这些更改仅反映在实时数据库中,而 Firestore 数据库中没有任何更改 users
集合,字符串“断开连接”已添加到实时数据库中,但我要连接的用户条目中没有任何更改在 then()
方法中更新。
在这里卡了2天,不知道自己做错了什么,只需要应用打开时online
字段为true
,用户是已登录,并且 false
当用户注销或应用程序在浏览器中关闭时。
按照您链接的文档中的 presence system,您将获得实时数据库中的状态信息。
要将该信息导入 Firestore,您需要实现一个云函数,该函数在实时数据库写入中触发,如 updating presence information in Firestore in the documentation on implementing presence on Firestore 部分所示。这是在 Firestore 中获取状态信息的唯一方法,因为该数据库没有构建状态系统所需的任何 onDisconnect
类似功能。
我正在尝试使用 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);
});
但这些更改仅反映在实时数据库中,而 Firestore 数据库中没有任何更改 users
集合,字符串“断开连接”已添加到实时数据库中,但我要连接的用户条目中没有任何更改在 then()
方法中更新。
在这里卡了2天,不知道自己做错了什么,只需要应用打开时online
字段为true
,用户是已登录,并且 false
当用户注销或应用程序在浏览器中关闭时。
按照您链接的文档中的 presence system,您将获得实时数据库中的状态信息。
要将该信息导入 Firestore,您需要实现一个云函数,该函数在实时数据库写入中触发,如 updating presence information in Firestore in the documentation on implementing presence on Firestore 部分所示。这是在 Firestore 中获取状态信息的唯一方法,因为该数据库没有构建状态系统所需的任何 onDisconnect
类似功能。