【问题标题】:Firebase v9 onSnapShot in async function while being called in useEffect ReactFirebase v9 onSnapShot 在异步函数中,同时在 useEffect React 中被调用
【发布时间】:2021-12-12 09:27:09
【问题描述】:

我正在使用 Firebase v9,我正在构建一个聊天应用程序,我需要在调用 onSnapShot 获取聊天数据之前做一些异步工作:

      const getChat = async () => {
        // I need to await for user data before doing anything else.
        // I'll need some user data to build the query which
        // I'll be using in onSnapShot later on.
        const user = await getUserData();
        console.log(user);
        
        const collectionRef = collection(db, "messages");
        const q = query(
        collectionRef, where("user_id", "==", user.id), orderBy("createdAt"));
    
        const unsubscribe = onSnapshot(q, (snapshot) => {
          const data = snapshot.docs.map((doc) => {
            const entry = doc.data();
    
            return {
              id: doc.id,
              message: entry.message,
            };
          });
          setChatLines(data);
        });
    
        return unsubscribe;
      };

然后在 useEffect 中:

  useEffect(() => {

    const unsubscribe = getChat();

    return () => {
      unsubscribe();
    };
  }, []);

这里的问题是unsubsribe 是一个promise,因为getChat 是异步的,但我仍然想在调用onSnapShot 之前等待getUserData(),因为稍后我将需要onSnapShot 查询中的用户数据.

有没有办法让这项工作同时能够在 useEffect 清理功能中正确unsubscribe

【问题讨论】:

    标签: reactjs firebase firebase-realtime-database react-hooks google-cloud-functions


    【解决方案1】:

    在这种情况下,我建议使用 React ref 来保存要在 useEffect 清理函数中使用的取消订阅函数。

    const unsubscribeRef = React.useRef();
    
    const getChat = async () => {
      const user = await getUserData();
       
      const collectionRef = collection(db, "messages");
      const q = query(
      collectionRef, where("user_id", "==", user.id), orderBy("createdAt"));
    
      const unsubscribe = onSnapshot(q, (snapshot) => {
        const data = snapshot.docs.map((doc) => {
          const entry = doc.data();
    
          return {
            id: doc.id,
            message: entry.message,
          };
        });
        setChatLines(data);
      });
    
      unsubscribeRef.current = unsubscribe;
    };
    
    useEffect(() => {
      getChat();
    
      return () => {
        unsubscribeRef.current?.();
      };
    }, []);
    

    另一种方法是在 useEffect 挂钩回调主体中添加一个额外的异步函数,该函数可以等待 getChat Promise 解决。

    useEffect(() => {
      let unsubscribe;
    
      const getChatAndSubscribe = async () => {
        unsubscribe = await getChat();
      }
        
      getChatAndSubscribe();
    
      return () => {
        unsubscribe?.();
      };
    }, []);
    

    【讨论】:

    • 我使用了第二种方法,但是当我在getChatAndSubscribe()之后console.log时,unsubsribe总是undefined,不应该是一个函数吗?
    • @dwix 请记住 getChatAndSubscribe 被声明为 asyncuseEffect 挂钩回调是完全同步的。当您在调用getChatAndSubscribe 后立即登录unsubscribe 时,它可能尚未更新。请注意,即使在钩子清理函数中,也使用了可选链接运算符来避免在组件卸载时 unsubscribe 仍未设置。
    • @dwix 检查它是否正常工作?我想当setChatLines 被调用(因为你取消订阅?而被调用)时,你不会看到任何关于内存泄漏等的 React 警告。您可以做的是在设置unsubscribe in getChatAndSubscribe 函数后,您应该会看到它是一个函数。您还可以在清理功能中添加日志或调试器,并在那里再次检查以进行验证。
    • 最后一个问题,如果可以的话。为什么不像第二种方法那样在第一种方法中使用简单变量?我不明白为什么在这种情况下你必须使用 useRef 而不仅仅是 let unsubsribe; 然后像你在第二种方法中那样做。
    • @dwix 第一个要求用于保存取消订阅函数的变量在both useEffect 钩子回调 @ 的范围内987654340@ 声明。如果你在组件中使用了一个简单的变量,它会在每个渲染周期被重置。 React ref 允许您保留一个可变变量,该变量会持续个渲染周期。您可以只在组件外部声明变量,但现在组件的 任何“实例”将引用相同的变量,这在 React 中被认为是反模式. (一提到它我不寒而栗
    猜你喜欢
    • 2019-11-12
    • 2017-08-13
    • 2020-10-07
    • 2021-04-23
    • 2021-05-02
    • 2020-08-08
    • 2022-11-03
    • 2020-09-02
    相关资源
    最近更新 更多