如何延迟 React Firebase Hooks 直到用户登录?

huangapple go评论70阅读模式
英文:

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

你可以创建两个包装组件,分别用于AuthenticatedAppUnauthenticatedApp,根据用户是否存在有条件地渲染其中一个,并将自定义钩子移动到仅在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:

https://www.robinwieruch.de/react-conditional-hooks/

答案2

得分: 0

正如你已经确定的那样,每个钩子必须在每次渲染时以完全相同的顺序调用,这意味着你不能在if块内调用useX。所以,与其考虑改变调用钩子的时机,你应该看看传递给该钩子的参数。特别是useCollection钩子接受firestore.Querynull作为其第一个参数。由于此值被馈送给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

huangapple
  • 本文由 发表于 2023年5月22日 04:06:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76301724.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定