Home > Net >  Firebase v9 onSnapShot in async function while being called in useEffect React
Firebase v9 onSnapShot in async function while being called in useEffect React

Time:10-27

I'm using Firebase v9, I'm building a chat app, I need to do some async work before calling onSnapShot to get the chat data :

      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;
      };

Then in useEffect :

  useEffect(() => {

    const unsubscribe = getChat();

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

The problem here is that unsubsribe is a promise, because getChat is async, but I still want to await getUserData() before onSnapShot is called, because I'll be needing the user data in the onSnapShot query later.

Is there a way to make this work while being able to unsubscribe correctly in useEffect cleanup function ?

CodePudding user response:

In this case I would suggest using a React ref to hold the unsubscribe function to be used in an useEffect cleanup function.

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?.();
  };
}, []);

An alternative would be an additional asynchronous function in the useEffect hook callback body that can wait for getChat Promise to resolve.

useEffect(() => {
  let unsubscribe;

  const getChatAndSubscribe = async () => {
    unsubscribe = await getChat();
  }
    
  getChatAndSubscribe();

  return () => {
    unsubscribe?.();
  };
}, []);
  • Related