1
votes

I'm building a chat app with firebase firestore database and reactjs

I'm trying to use react hooks to manage the data flow but am having trouble detaching the firestore listener.

This use effect is where I am initialising the listener.

useEffect(() => {
    setMessages(null)

    roomId && listenForMessages(roomId, setMessages, false, scrollToBottom)
    return () => {
      if (active && setMessages) {
        //Detach here
        listenForMessages(roomId, setMessages, true)
      }
    }
  }, [roomId ])

This is my listener function

export const listenForMessages = (roomId, setMessages, detach, scrollToBottom) => {
  let listener = db
    .collection('chats')
    .doc(roomId)
    .collection('messages')
    .orderBy('timestamp')
    .onSnapshot(
      docSnapshot => {
        let data = []
        docSnapshot.docs.forEach(doc => data.push({ id: doc.id, ...doc.data() }))
        setMessages(data)
        scrollToBottom()
      },
      err => {
        console.log(`Encountered error: ${err}`)
      }
    )
  if (detach === true) {
    listener()
  }
}

This dosnt seem to detach previously attached listeners. As if I open chat room A then navigate to chat room B and chat room A gets a message, it will call over writting messages.

1

1 Answers

2
votes

This ended up working but its not very clean if anyone can restructure it let me know.

I have identified that the initial code was closing the listener but calling the listenForMessages() function again to close it was opening another listener.

useEffect(() => {
    setMessages(null)
    let listener
    if (active) {
      listener = db
        .collection('chats')
        .doc(active)
        .collection('messages')
        .orderBy('timestamp')
        .onSnapshot(
          docSnapshot => {
            let data = []
            docSnapshot.docs.forEach(doc => data.push({ id: doc.id, ...doc.data() }))
            setMessages(data)
            scrollToBottom()
          },
          err => {
            console.log(`Encountered error: ${err}`)
          }
        )
    }
    return () => {
      if (active) {
        listener()
      }
    }

    //eslint-disable-next-line
  }, [active])