英文:
How can I delay React Firebase Hooks until the user is logged in?
问题
如何推迟 React Firebase Hooks 直到知道用户是否已登录(类似于 React Query 中 useQuery 的 "enabled" 属性)
我需要从 Firebase 获取数据,但只想在用户已登录时获取数据。在 React Query 中,有一个选项 "enabled" 属性,可以传递以推迟调用直到满足特定条件为止。这正是我对 React Firebase Hooks 需要的。
取下面的代码:
const [recentForumPosts, loadingRecentForumPosts, errorRecentForumPosts] = useCollection(query(collection(db, "forum_posts", where("userId", '==', user.userFirebase.uid))));
它使用用户的 uid 从 Firestore 获取数据,但这可能是 null 或 undefined(如果用户未登录)。然而,API 调用仍会尝试使用 null id 获取此信息,导致 API 调用浪费和日志中的错误。
我尝试将它放在 useEffect 中,但 React 抛出一个错误,说不能在回调中使用 hooks。
useEffect(() => { const [recentForumPosts, loadingRecentForumPosts, errorRecentForumPosts] = useCollection(query(collection(db, "forum_posts", where("userId", '==', user.userFirebase.uid)))); }, [user])
我还尝试在 hook 之前放置一个 IF 语句,但 React 抛出一个错误,说不能有条件地使用 React Hook。
if (user) { const [recentForumPosts, loadingRecentForumPosts, errorRecentForumPosts] = useCollection(query(collection(db, "forum_posts", where("userId", '==', user.userFirebase.uid)))); }
我正在使用上下文在应用程序的顶层存储用户对象,并在整个应用程序中使用它。基本上,如果它为 null 或 undefined,则用户未登录,如果它有数据,则用户已登录。
这似乎是 React Firebase Hooks 的一个非常常见的用例(获取属于用户的数据),但我很难在用户已登录或未登录时有条件地调用这个 hook。
英文:
How to delay React Firebase Hooks until it is known if user is logged in (similar to how enabled works on React Query useQuery)
I need to get data from firebase, but only want to get the data if the user is logged in. In React Query, there is an option "enabled" property that can be passed to delay the call until a certain condition is true. This is exactly what I need but for the React Firebase Hooks.
Take the code below:
const [recentForumPosts, laodingRecentForumPosts, errorRecentForumPosts] = useCollection(query(collection(db, "forum_posts", where("userId", '==', user.userFirebase.uid))));
Its using the users' uid to get the data from firestore, but this may be null || undefined (if the user is not logged in). However the API call with still attempt to get this info with null id resulting in a wasted API call and error in the log.
I've tried putting it in a useEffect but react throws an error that hooks cannot be used inside a callback.
useEffect(() => { const [recentForumPosts, laodingRecentForumPosts, errorRecentForumPosts] = useCollection(query(collection(db, "forum_posts", where("userId", '==', user.userFirebase.uid)))); }, [user])
I've also tried putting an IF statment before the hook but react throws an error that a React Hook cannot be used conditionally.
if (user) { const [recentForumPosts, laodingRecentForumPosts, errorRecentForumPosts] = useCollection(query(collection(db, "forum_posts", where("userId", '==', user.userFirebase.uid))));}
I am using context to store the user object at the top level of the app and using it throughout the app. Essentially if its null or undefined, then user is not logged in, and if it has data, user is logged in.
This seems like a pretty common use case for React Firebase Hooks, (Getting data that belongs to a user) but I am struggling to conditionally call this this hook if they are logged in or not.
答案1
得分: 0
你可以创建两个包装组件,分别用于AuthenticatedApp
和UnauthenticatedApp
,根据用户是否存在有条件地渲染其中一个,并将自定义钩子移动到仅在AuthenticatedApp
内部调用。通过将钩子移到有条件地渲染的子组件中,你可以实现你的用例。
详细说明此模式的内容请参见:
https://www.robinwieruch.de/react-conditional-hooks/
英文:
You can create two wrapper components for AuthenticatedApp
and UnauthenticatedApp
and conditionally render one or the other depending on whether the user is present and move the custom hook so that it is only called inside the AuthenticatedApp
. By moving the hook into a conditionally rendered child component, you can achieve your use case.
See a detailed explanation of this pattern here:
答案2
得分: 0
正如你已经确定的那样,每个钩子必须在每次渲染时以完全相同的顺序调用,这意味着你不能在if
块内调用useX
。所以,与其考虑改变调用钩子的时机,你应该看看传递给该钩子的参数。特别是useCollection
钩子接受firestore.Query
或null
作为其第一个参数。由于此值被馈送给useRef()
内部,你可以根据需要在每次渲染时更改其值。
const recentForumPostsQuery = user
? query(collection(db, "forum_posts", where("userId", '==', user.userFirebase.uid)))
: null; // 可以回退到公共动态?
const [recentForumPosts, loadingRecentForumPosts, errorRecentForumPosts] =
useCollection(recentForumPostsQuery); // useCollectionData也可能有用
// 查询为null?recentForumPosts将始终未定义
// 给定查询?在成功获取数据之前,recentForumPosts将为undefined,
// 其后将为QuerySnapshot
英文:
As you've identified, each hook must always be called in the exact same order on each render which means you can't call useX
inside of an if
block. So instead of looking at changing when the hook is called, you should instead look at the arguments passed into that hook. In particular, the useCollection
hook accepts a firestore.Query
or null
as its first argument. As this value is fed to useRef()
internally, you can change its value on each render as needed.
const recentForumPostsQuery = user
? query(collection(db, "forum_posts", where("userId", '==', user.userFirebase.uid)))
: null; // maybe fallback to a public feed?
const [recentForumPosts, loadingRecentForumPosts, errorRecentForumPosts] =
useCollection(recentForumPostsQuery); // useCollectionData may also be of use
// query is null? recentForumPosts will always be undefined
// query is given? recentForumPosts will be undefined until data is successfully fetched,
// where it will be a QuerySnapshot
if (loadingRecentForumPosts)
return null; // example: hide until data is loaded
if (!recentForumPosts)
return (
<div class="error">
You must login first to see your recent posts!
</div>
);
// TODO: render posts
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论