【问题标题】:Firestore onSnapshot "IN" condition in batches (conditioned realtime updates)Firestore onSnapshot "IN" 批量条件(有条件的实时更新)
【发布时间】:2021-08-04 14:34:32
【问题描述】:

TL;DR

我正在开发聊天列表功能,就像任何大型社交网络一样,我在 React Native 状态管理方面遇到了问题,因为 very common problem with Firestore onSnapshot "in" conditions.

作为解决方法,我正在从状态数组生成批处理。onSnapshot 根据此类批处理更改状态数组,但是每次更改后我都无法刷新批处理。

完整说明

其中一个复杂性是我必须以 Firebase 尚不支持的方式调节来自 Firestore 的实时更新:

    const watchedGroups = db.collection('group').where('__name__', 'in', groupArray?.map(({ id }) => id));
        unsubscribeListener = watchedGroups.onSnapshot((querySnapshot) => {
        querySnapshot.forEach((doc) => {
        //...

(请注意群组 = 聊天)

这种方法的问题是Firestore does not support a IN condition (groupArray) with more than 10 elements 和这个代码块会在情况发生时崩溃。

为了解决这个问题,我在不违反此类限制的批次中联系了groupArray

  const [recentChats, setRecentChats] = useState([]);
  // ...
  useFocusEffect(useCallback(() => {
    const grupos = [...recentChats];
    if (grupos && grupos.length > 0) {
      handleRefreshSuscriptions();
      const collectionPath = db.collection('group');
      while (grupos.length) {
        const batch = grupos.splice(0, 10);
        console.log(">> QUERYING", batch?.length, batch.map(({ lastMsgForMe }) => lastMsgForMe))

        const unsuscribe = collectionPath.where(
          '__name__',
          'in',
          [...batch].map(({ id }) => id)
        ).onSnapshot((querySnapshot) => {

          if (querySnapshot !== null) {
            querySnapshot.forEach((doc) => {
              const validGroup = batch.find(grupo => doc.id == grupo.id);

              if (validGroup) {
                lastMsg(doc.id).then((lastM) => {

                  console.log(batch.map(({ lastMsgForMe }) => lastMsgForMe))

                  if (validGroup.lastMsgForMe !== doc.data().recentMessage.messageText) {
                    mergeChat({
                      ...validGroup,
                      messageText: doc.data().recentMessage.messageText,
                      lastMsgForMe: lastM.messageText,
                      dateMessageText: lastM.sentAt,
                      viewed: lastM.viewed
                    });
                  }
                }).catch(error => console.log(error));
              }
            })
          }
        })
        setRefreshSuscription(prevState => [...prevState].concat(unsuscribe))
      }
    }
    return () => {
      handleRefreshSuscriptions();
    }
  }, [recentChats.length]));

它(几乎)完美地工作,每次更改都成功地到达视图。但是,有一个问题,这是我收到第一次更新时的日志:

// Initialization (12 groups shown, 2 batches)
>> QUERYING 10 ["B", "Dddffg", "Dfff", ".", null, "Hvjuvkbn", "Sdsdx", "Vuvifdfhñ", "Ibbijn", "asdasdasd"]
>> QUERYING 2 ["Veremoss", "Hjjj"]

// Reception of a message "C" that updates last message shown ("B") of first group in the list.
["B", "Dddffg", "Dfff", ".", null, "Hvjuvkbn", "Sdsdx", "Vuvifdfhñ", "Ibbijn", "asdasdasd"] //several repetitions of this log, i've erased it for simplicity
update idx 0 - B -> C

此时,没有任何明显的问题。但是,如果我继续与其他组交互,然后在收到上述组的消息时注意日志,我会看到:

["B", "Dddffg", "Dfff", ".", null, "Hvjuvkbn", "Sdsdx", "Vuvifdfhñ", "Ibbijn", "asdasdasd"]
update idx 1 - Bbnnm -> Bbnnm // unexpected
update idx 0 - 12 -> 12 // unexpected
update idx 2 - C -> D // expected

请注意,当我已经收到该组的“C”和“D”消息时,该批次仍然显示“B”。这个问题在其他两组上重复出现,正因为如此,现在我得到了一个真正的变化和另外两个误报。

问题在于,由于批次的生成方式,onSnapshot 内部的batch 内容始终相同。这会导致与自批次生成以来已更新的组一样多的错误“更新”,每条收到的消息。

如何在onSnapshot 中保持批次是最新的?

【问题讨论】:

    标签: javascript reactjs firebase google-cloud-firestore


    【解决方案1】:

    我提供的一个可能的解决方案是随时更新批次,方法是从 find 切换到 findIndex 并在 batch 中进行更新

    querySnapshot.forEach((doc) => {
      const validGroupIdx = batch.findIndex(grupo => doc.id == grupo.id);
      if (validGroupIdx !== -1) {
        lastMsg(doc.id).then((lastM) => {
          console.log(batch.map(({ lastMsgForMe }) => lastMsgForMe))
          if (batch[validGroupIdx].lastMsgForMe !== doc.data().recentMessage.messageText) {
            batch[validGroupIdx] = {
              ...batch[validGroupIdx],
              messageText: doc.data().recentMessage.messageText,
              lastMsgForMe: lastM.messageText,
              dateMessageText: lastM.sentAt,
              viewed: lastM.viewed
            }
            mergeChat(batch[validGroupIdx]);
          }
        }).catch(error => console.log(error));
      }
    })
    

    但是,据我了解,这仍然不是最理想的,因为当我导航到其他组件时,我会得到旧批次而不是更新的批次,至少会引发一次误报。

    我想知道我是否可以直接批量处理状态,而不是从中生成批次。 但是,对它进行排序和合并会很痛苦。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-17
      • 2020-11-08
      • 1970-01-01
      • 2011-01-08
      • 2018-11-12
      • 1970-01-01
      • 2019-09-14
      • 2019-03-03
      相关资源
      最近更新 更多