我正在将一个项目从 Angular 重写为 ReactJS,所以我对它应该如何工作有一些预先存在的想法: 我正在尝试使用当前的 firebase.auth() 从 Firestore 获取我的用户文档并将其保存为静态对象(以避免对 Firebase 的冗余调用)。
在 Angular 中,您可以使用服务文件(单例)来监听 firebase.auth() 状态,使用 currentUser.uid 调用 firestore,并存储 doc 对象以供任何组件抓取和使用。
在 React 中,似乎正确的方法是通过全局状态,或者我决定采用的方法,一个自定义钩子。
// Custom Hook useCurrentUser
import { doc, onSnapshot } from 'firebase/firestore';
import { useEffect, useState } from 'react';
import firebase from '../Firebase';
const db = firebase.firestore();
export default function useCurrentUser() {
const [authUser, setAuthUser] = useState(null);
const [user, setUser] = useState(null);
// Listen to Auth User
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(u => {
console.log('authStateChanged', u);
setAuthUser(u);
});
return unsubscribe();
});
// Get User doc from Firestore
useEffect(() => {
let subscription;
console.log('authUser', authUser);
if (authUser) {
const docRef = doc(db, 'users', authUser.uid);
subscription = onSnapshot(docRef, snapshot => {
const data = snapshot.data();
console.log('CurrentUserHook', data);
setUser(data);
});
} else {
setUser(null);
}
return () => {
if (subscription) subscription();
};
}, [authUser]);
return user;
}
// Firebase.js
import firebase from 'firebase/compat/app';
const firebaseConfig = { /* config stuff */ };
firebase.initializeApp(firebaseConfig);
export default firebase;
// Component implementing useCurrentUser
function UserMenu() {
const user = useCurrentUser();
return (
<div>
{ /* This is a menu that closes on click */ }
{user ?
<LoggedInMenu user={user} /> :
<LoggedOutMenu />}
</div>
);
}
问题:单击该 UserMenu 中的按钮(如 Login 或 Logout)后,菜单关闭(不再呈现),并且在 useCurrentUser() 中实际接收新 AuthState 的最终操作不会发生。因此,用户单击 logout(),菜单关闭,并且用户保持登录状态,直到再次单击该菜单以调用渲染并让这些 useEffects() 再次运行。
这感觉像是一种非常干净、简单的方法来处理身份验证和获取我的自定义用户文档,但我缺少最后一点让它完全工作。我需要再进行一个渲染循环,但我不知道该怎么做。或者,在 React 中可能有更好的方法来实现这个目标。