-1

As the title says. I'm using firestore for my chat service. I'm attempting to set up a way to filter out messages when the firestore updates

  const users = useSelector((state) => state.assignUser);

  const [messages, setMessages] = useState([]);

  const chatID = 'KfM3GNxeVATi6sNFFUEG'

  function getIDs(messageObj){
    return messageObj.map(message => message._id)
  }

  useEffect(() => {
    const messagesArray = []

    db.collection('messages').doc(chatID).collection('messages').get().then(snapshot => {
      snapshot.forEach(doc => messagesArray.push(doc.data()))
      setMessages(messagesArray)

      async function fetchMessages(){
        const messageIDs = await getIDs(messages)
        db.collection('messages').doc(chatID).collection('messages').onSnapshot(doc => {
          doc.forEach((document) => {
            if (messageIDs.indexOf(document.data()._id !== -1)) console.log('the is is ', messageIDs.indexOf(document.data()._id))
            else console.log('notfoundssw')
          })
        })
      }
      fetchMessages()
    })
  }, [])

The await doesn't await the getIDs function and the log of messageIDs.indexOf(document.data()._id) is -1 for every entry. I can't figure out why the async/await missbehaves

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Mislav
  • 391
  • 5
  • 17
  • 2
    It doesn't look like there's nothing to await for since Array.prototype.map is a fully synchronous function – slebetman Feb 24 '21 at 18:34
  • Mapping the ids in the `fetchMessages` would still error out and cause the logging of `messageIDs.indexOf(document.data()._id)` to be -1 – Mislav Feb 24 '21 at 18:36
  • 2
    There's a typo in the code, it should be: `if (messageIDs.indexOf(document.data()._id) !== -1)` – Emile Bergeron Feb 24 '21 at 18:38
  • Thanks @EmileBergeron I didn't notice it. but still the original issue persists – Mislav Feb 24 '21 at 18:41
  • 1
    Are you expecting `setMessages` to immediately change the value of messages? That's not how it works. It will only happen in the next render cycle. If you want to listen to changes to `messages` you need to use another useEffect. Specifically `useEffect(functionThatCallGetIds, [messages])` – slebetman Feb 24 '21 at 18:47
  • See: [useState set method not reflecting change immediately](https://stackoverflow.com/q/54069253/1218980) – Emile Bergeron Feb 24 '21 at 18:49

2 Answers2

3

your messagesIDs variable depends on messages state update, which does not happen sync. Based on your code structure you would have to break in 2 useEffects. The first one that updates messages on mount and the second that listens to messages updates:

  useEffect(() => {
    const messagesArray = []

    db.collection('messages').doc(chatID).collection('messages').get().then(snapshot => {
      snapshot.forEach(doc => messagesArray.push(doc.data()))
      setMessages(messagesArray)
    })
  }, [])


  useEffect(() => {
    // check if messages is not empty
    if(!messages.length) return

    const messageIDs = getIDs(messages) // your getIDs is not async, so no need to await
    db.collection('messages').doc(chatID).collection('messages').onSnapshot(doc => {
      doc.forEach((document) => {
        if (messageIDs.indexOf(document.data()._id) !== -1) {
          console.log('the is is ', messageIDs.indexOf(document.data()._id))
        } else {
          console.log('notfoundssw')
        }
      })
    })
}, [messages])
buzatto
  • 9,704
  • 5
  • 24
  • 33
1

Not sure I can fully understand how you're getting data, but setting state is actually asynchronous so you cannot guarantee all your state data is updated by the time you execute map.

I would break up your useEffect into two different ones.

The second one which will contain the fetchMessages call should use 'messages' as part of its dependency array, i.e. it will execute once your 'messages' state has been updated.

Clinton Chau
  • 557
  • 2
  • 12