【问题标题】:Firebase: Problem getting data from nested onSnapshotFirebase:从嵌套的 onSnapshot 获取数据时出现问题
【发布时间】:2021-05-26 09:15:11
【问题描述】:

我设置了以下 useEffect 以从 firebase 获取一些数据以填充平面列表

useEffect(() => {
  return db
    .collection('accounts')
    .doc(currentAccountId)
    .collection('shops')
    .doc(currentShopId)
    .collection('sensors')
    .onSnapshot((querySnapshot) => {
      const list = [];

      querySnapshot.forEach((doc) => {
        
        const { sensorReadingsId } = doc.data();

        // This works, but doesn't load the additional data I need.
        // list.push({ ...{ id: doc.id }, ...doc.data() });

        // This doesn't seem to load anything.
        db.collection('sensors')
          .doc(sensorReadingsId.toString())
          .onSnapshot((documentSnapshot) => {
            list.push({ ...{ id: doc.id }, ...doc.data(), ...documentSnapshot.data() });
          });

      });

      setSensorsData(list);
      console.log(sensorsData);
      setLoading(false);

    });
}, [currentAccountId, currentShopId]);

问题是:我的平面列表没有加载任何内容。当我最初加载屏幕时,console.log 输出一个空数组。如果我通过保存屏幕文件强制它重新渲染,则数组已正确填充。

如评论中所述,如果我只使用list.push({ ...{ id: doc.id }, ...doc.data() }); 而不是db.collection('sensors')...,它会很好地加载传感器,但没有我需要的额外数据。

我该如何解决这个问题?提前致谢!


解决方案:正如@Rajitha Udayanga 所指出的,根据列表数组检查第一个查询快照的长度是检查是否所有数据都已加载的好方法。这是工作代码(注意setSensorsData 已被移入的条件):

useEffect(() => {
  return db
    .collection('accounts')
    .doc(currentAccountId)
    .collection('shops')
    .doc(currentShopId)
    .collection('sensors')
    .onSnapshot((querySnapshot) => {
      const list = [];

      querySnapshot.forEach((doc) => {
        
        const { sensorReadingsId } = doc.data();

        db.collection('sensors')
          .doc(sensorReadingsId.toString())
          .onSnapshot((documentSnapshot) => {
            list.push({ ...{ id: doc.id }, ...doc.data(), ...documentSnapshot.data() });
            
            // Fix is here:
            if (querySnapshot.docs.length === list.length) {
              setSensorsData(list);
              console.log(sensorsData);
              setLoading(false);
            }

          });

      });

    });
}, [currentAccountId, currentShopId]);

【问题讨论】:

    标签: reactjs firebase react-native google-cloud-firestore react-hooks


    【解决方案1】:

    免责声明:我对 React 的经验为零。

    在 Angular 中,我使用 RxJS 的 combineLatest 来处理此类问题。

    不过,你可以试试这个:

    useEffect(() => {
      return db
        .collection('accounts')
        .doc(currentAccountId)
        .collection('shops')
        .doc(currentShopId)
        .collection('sensors')
        .onSnapshot((querySnapshot) => {
          const list = [];
          const useValues = () => {
            if (list.some(value => value === undefined)) {
              return; // not everything has loaded yet - abort
            }
            setSensorsData(list);
            console.log(sensorsData);
            setLoading(false);
          }
          querySnapshot.forEach((doc, index) => {
            list.push(undefined);
            const { sensorReadingsId } = doc.data();
            db.collection('sensors')
              .doc(sensorReadingsId.toString())
              .onSnapshot((documentSnapshot) => {
                list[index] = ({ id: doc.id, ...doc.data(), ...documentSnapshot.data() });
                useValues();
              });
          });
        });
    }, [currentAccountId, currentShopId]);
    

    当您不再需要它们时,不要忘记detach the listeners

    【讨论】:

      【解决方案2】:
      useEffect(() => {
        return db
          .collection('accounts')
          .doc(currentAccountId)
          .collection('shops')
          .doc(currentShopId)
          .collection('sensors')
          .onSnapshot((querySnapshot) => {
            const list = [];
      
        querySnapshot.forEach((doc) => {
          
          const { sensorReadingsId } = doc.data();
      
          db.collection('sensors')
            .doc(sensorReadingsId.toString())
            .onSnapshot((documentSnapshot) => {
           
              const newData = { 
                 ...{ id: doc.id }, 
                 ...doc.data(), 
                 ...documentSnapshot.data() 
              };
      
              const itemIndex = list.findIndex(item => item.id === 
                    newData.id);
      
              if (itemIndex !== -1) {
                    list[itemIndex] = newData;
              } else {
                    list.push(newData);
              }
      
              // do it like thi
              if (querySnapshot.docs.length === list.length) {
                 setSensorsData(list);
                 console.log(sensorsData);
                 setLoading(false);
              }
            });
      
        });
      
      });
      }, [currentAccountId, currentShopId]);
      

      【讨论】:

      • 如果传感器数据已经添加到您的list 数组中,您可以更新现有对象,否则,您可以添加新数据。
      • 现在您假设内部回调将以正确的顺序触发,这不太可能。
      • 是的,没错。但我认为这会奏效。顺序无关紧要。
      • 这太棒了,只需稍作修改即可工作:其中有一些不需要的额外复杂性。查看我对问题的补充
      猜你喜欢
      • 2019-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-29
      • 2022-06-15
      • 1970-01-01
      • 2017-07-13
      • 1970-01-01
      相关资源
      最近更新 更多