我正在编写一个 ReactJS 应用程序,其中有一个提供程序类 (AuthProvider.js) 处理 Firebase 身份验证。它的几个子级使用 auth 状态。大约一个月以来一切都工作正常,但上周末我们开始遇到问题,可能是因为一些升级,我正在尝试排除故障。基本上,在 AuthProvider 组件中,我有一个
currentUser
状态,当没有人登录时该状态应该为 null,或者当有人登录时包含有关当前用户的一些数据。可能只是 UID。可能是完整的用户对象。可能是一个布尔值。在这一点上我们并不挑剔。基本上,当我尝试教科书方法时
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
setCurrentUser(user);
});
// Cleanup subscription on unmount
return () => unsubscribe();
}, []);
在 AuthProvider 类(有或没有 useEffect)中,我得到“渲染的钩子比预期少”。我听到一些关于在回调函数中使用 setState 是一个坏主意的陈词滥调,但是在设置
errors
(另一个像 currentUser
一样导出的状态对象)时没有任何问题。
是否有一种安全可靠的方法可以让我从 AuthProvider 的子组件观察 firebase 状态,并且代码重复最少?通常,我有几种情况,向客人展示一个组件,向成员展示另一个组件,因此状态对我来说很重要。
试试这个:
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if(user){
setCurrentUser(user);
}else{
setCurrentUser(null);
}
});
// Cleanup subscription on unmount
return () => unsubscribe();
}, []);
我认为最好的方法是使用 context-api 或 redux 来使用全局状态。因为用户状态在你的项目中是一个非常重要的状态。当您有更多组件时,您必须将一个组件的用户状态传递给另一组件。我认为这不是一个好的做法,你不能每次都这样做。
如果您使用我上面提到的任何状态管理系统,那么您可以从 onAuthStateChanged 获取经过身份验证的用户并将其分派到全局状态。然后您可以在项目中的任何地方使用该状态。
useEffect(() => {
auth.onAuthStateChanged(userAuth => {
dispatch({
type: actionTypes.SET_USER,
user: userAuth,
})
})
}, [])
注意:这不是完整的代码,您首先必须在 redux 或 context-api 中创建减速器和其他减速器。然后就可以用这个了。
我发现更干净、更实用的另一种方法是在单独的文件“useAuthHook.ts”上使用自定义挂钩,然后在组件上使用它:
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>
import { useEffect, useMemo, useState } from "react"
import { auth } from "../services/firebaseAuth"
import { User } from "firebase/auth"
export const useAuth = () => {
const [authStateLoading, setauthStateLoading] = useState<boolean>(true);
const [userAuth, setUserAuth] = useState<User | null | undefined>(undefined);
const isUserAuthenticated: boolean | undefined = useMemo(() => {
if (authStateLoading) {
return undefined;
}
return userAuth !== null;
}, [userAuth]);
useEffect(() => auth.onAuthStateChanged((userAuth: User | null) => {
setUserAuth(userAuth);
if (authStateLoading) {
setauthStateLoading(false);
}
}), [])
return {
authStateLoading, userAuth, isUserAuthenticated
}
}
这是我们如何在组件中使用的示例:
//.....
// Before rendering component, if "Auth State was checked" and "User not authenticated" => redirect to login page
if (!authStateLoading && requiresAuth && !isUserAuthenticated) {
redirectUnauthorized();
}
// Rendering component
return authStateLoading
? <Loader message="Pending..." />
: <div>MY COMPONENT</div>
希望有帮助!