【问题标题】:How to wait for Firebase data to be fetched before progressing?如何在继续之前等待获取 Firebase 数据?
【发布时间】:2021-04-26 02:21:57
【问题描述】:

我正在使用 .onSnapshot 从 Fire 存储中实时获取数据,效果很好,我正在按预期接收数据。问题是我正在接收多组数据,组件并没有等到接收到所有数据才渲染。

所以我的问题是,对于我当前的代码,它们是否可以让我在显示它们之前等待获取所有数据集?

我当前的代码是:

import React, {useEffect, useState} from 'react';
import {ActivityIndicator, Dimensions, Text, View} from 'react-native';
import firestore from '@react-native-firebase/firestore';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import FolloweringScreens from './FolloweringScreens';
import {TouchableOpacity} from 'react-native-gesture-handler';

const {width, height} = Dimensions.get('screen');

function Following({urlname, navigation}) {
  const [followingData, setfollowingData] = useState([]);

  // Follower counts, displayname, image
  const fetchData = () => {
    const dataRef = firestore().collection('usernames');

    dataRef
      .doc(urlname)
      .collection('Following')
      .onSnapshot((snapshot) => {
        snapshot.forEach((doc) => {
          dataRef.doc(doc.id.toLowerCase()).onSnapshot((followerDoc) => {
            const data = followerDoc.data();
            setfollowingData((prev) => [
              ...prev,
              {
                profileName: doc.id,
                displayName: data.userName,
                followerCount:
                  data.followers !== undefined ? data.followers : 0,
                followingCount:
                  data.following !== undefined ? data.following : 0,
                image: data.imageUrl ? data.imageUrl : null,
              },
            ]);
          });
        });
      });
  };

  useEffect(() => {
    fetchData();
  }, []);

  return (
    <>
      <View
        style={{
          left: width * 0.04,
          top: 50,
          flexDirection: 'row',
          alignItems: 'center',
          width: '80%',
          height: '4%',
          marginBottom: 5,
        }}>
        {/* {console.log('followin', followingData)} */}
        <TouchableOpacity onPress={() => navigation.openDrawer()}>
          <Icon name="menu" color="#222" size={30} />
        </TouchableOpacity>
        <Text style={{left: width * 0.05}}>Following</Text>
      </View>

      {followingData === [] ? (
        <ActivityIndicator size="large" color="black" />
      ) : (
        <>
          <FolloweringScreens data={followingData} />
        </>
      )}
    </>
  );
}

export default Following;

【问题讨论】:

  • 使用加载状态,初始为 false,然后在快照上设置为 true,直到加载后才显示 ui。不要使用onSnapshot,如果你想获取一次数据,只需使用get
  • 我希望每当数据发生变化时显示也会发生变化,所以我需要使用onSnapshot。或者他们是一种使用 get() 并在数据更改时更新组件的方法?因为当我使用 get() 时,我可以通过 promise 等待所有数据被获取,但是当数据发生变化时,你必须手动刷新才能显示更改。
  • 然后坚持使用 onSnapshot,使用状态 isLoading 默认 false,然后在快照解析后将 isLoading 设置为 true,并在 isLoading true 上显示加载指示器,并在 isLoading false 时显示您的 ui。然后您将更新推送到您的状态,一旦数据完全加载,用户就会看到数据
  • 当 dong this 时,会显示加载器,并且一旦获取了第一组数据,就会加载 UI,其他组数据会依次显示,而不是所有数据获取,然后显示 UI。我应该在哪里触发 setloading?不幸的是,似乎在获取第一组数据后立即触发状态更改。

标签: javascript react-native google-cloud-firestore promise fetch


【解决方案1】:

所以我设法以某种方式修复它。感谢朱利安的帮助

我所做的是创建一个承诺数组,只要数据发生变化就会执行。代码是:

import React, {useCallback, useEffect, useState} from 'react';
import {ActivityIndicator, Dimensions, Text, View} from 'react-native';
import firestore from '@react-native-firebase/firestore';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import FolloweringScreens from './FolloweringScreens';
import {TouchableOpacity} from 'react-native-gesture-handler';

const {width, height} = Dimensions.get('screen');

function Following({urlname, navigation}) {
  const [followingData, setfollowingData] = useState();
  const [loading, setLoading] = useState(true);

  // Following counts, displayname, image
  const fetchData = useCallback(() => {
    const dataRef = firestore().collection('usernames');

    dataRef
      .doc(urlname)
      .collection('Following')
      .limit(25)
      .onSnapshot((snapshot) => {
        let promises = [];
        snapshot.forEach((doc) => {
          const promise = dataRef
            .doc(doc.id.toLowerCase())
            .get()
            .then((followerDoc) => {
              const data = followerDoc.data();

              return {
                profileName: doc.id,
                displayName: data.displayName
                  ? data.displayName
                  : data.userName,
                followerCount:
                  data.followers !== undefined ? data.followers : 0,
                followingCount:
                  data.following !== undefined ? data.following : 0,
                image: data.imageUrl ? data.imageUrl : null,
              };
            });
          promises.push(promise);
        });
        Promise.all(promises)
          .then((res) => setfollowingData(res))
          .then(setLoading(false));
      });
  }, []);

  useEffect(() => {
  const dataRef = firestore().collection('usernames');

    const cleanup = dataRef
      .doc(urlname)
      .collection('Following')
      .limit(25)
      .onSnapshot(fetchData);

    return cleanup;

    // fetchData();
  }, [urlname, fetchData]);

  return (
    <>
      <View
        style={styles}>
        <TouchableOpacity onPress={() => navigation.openDrawer()}>
          <Icon name="menu" color="#222" size={30} />
        </TouchableOpacity>
        <Text style={{left: width * 0.05}}>Following</Text>
          </View>

      {loading ? (
        <ActivityIndicator size="large" color="black" />
      ) : (
        <>
          <FolloweringScreens data={followingData} />
        </>
      )}
    </>
  );
}

export default Following;

【讨论】:

  • 好吧,哇哦 :D 这里有些事情真的不对。对于初学者来说习惯了 async/await,它只是创建了一个更简洁的代码。不要使用 forEach 用 promise 填充数组,只需使用 map 并返回 promise。不要在 useEffect 挂钩之外定义 fetchData 函数,它缺少依赖项。查看我的解决方案,其中函数声明位于 useCallback 挂钩中
  • 我必须指出这不是最佳的。 dataRef 是收藏吗?因此,当您获得一个集合时,您可以使用 forEach 遍历集合中的文档,每个 QueryDocumentSnapshot 都有一个 ref 属性,它返回一个 DocumentReference,您可以使用它来引用文档或在您想要的情况下使用它获取数据用snapshot.data()获取,请参考googleapis.dev/nodejs/firestore/latest/…
  • 我还将在usernames 集合中的文档中存储一个DocumentReferenceFollowing 集合中的文档中
  • 然后您从快照中获取数据,或者仅获取您感兴趣的属性(节省带宽并降低成本)作为参考,您可以通过在此参考上运行 get 来创建承诺
  • 不客气,继续前进。您可以将DocumentReference 保存到Document 中的属性中,当您获取此属性时,您可以直接获取快照,而无需重新创建引用
【解决方案2】:

使用状态 isLoading 默认为 true,然后在快照解析后将 isLoading 设置为 false,并在 isLoading 为 true 时显示加载指示器,并在 isLoading 为 false 时显示您的用户界面。然后您将更新推送到您的状态,一旦数据完全加载,用户就会看到数据。

也会使用接近这个的东西。一件很奇怪的事情是,您将每个快照更改推送到一个数组,所以换句话说,随着时间的推移,这个数组保存了同一对象的更改历史。故意的?

function Following({ urlname }) {
  const [followingData, setfollowingData] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  // Follower counts, displayname, image
  const onSnapshot = useCallback((snapshot) => {
    snapshot.forEach((doc) => {
      dataRef.doc(doc.id.toLowerCase()).onSnapshot((followerDoc) => {
        const data = followerDoc.data();
        // push new document data into an array
        setfollowingData((prev) => [
          ...prev,
          {
            profileName: doc.id,
            displayName: data.userName,
            followerCount: data.followers !== undefined ? data.followers : 0,
            followingCount: data.following !== undefined ? data.following : 0,
            image: data.imageUrl ? data.imageUrl : null
          }
        ]);
        // or set the new data to state, by just setting the document data
        setfollowingData(data);
        setIsLoading(false);
      });
    });
  }, []);

  useEffect(() => {
    const dataRef = firestore().collection("usernames");

    const cleanup = dataRef
      .doc(urlname)
      .collection("Following")
      .onSnapshot(onSnapshot);

    return cleanup;
  }, [onSnapshot, urlname]);

  return (
    <>
      {isLoading && <p>Loading</p>}
      {!isLoading && <p>Show data {followingData.length}</p>}
    </>
  );
}

【讨论】:

  • 所以这解决了所有问题,但是你之后所说的,每个快照更改都被推送到一个数组,因此保存更改历史不是故意的,这实际上使得每当更改发生时制作,重复数据被创建。我怎样才能使对象在进行更改时更新?而不是将数据添加到数组中?非常感谢
  • 我编辑了我的答案并放了两个示例,一个将新文档数据推送到一个数组(现有)以及如何将新数据设置为状态,以便您可以自动更新您的 ui 如果数据在 Firestore 中更改
  • 所以我将我的数据推送到一个平面列表中,我认为它因此需要在一个数组中。但是第一个选项带来了同样的问题,每当进行更改时,都会添加一组新数据,而不是替换。
  • 如何从 Firestore 文档中获取平面列表?你想收藏吗?
猜你喜欢
  • 2021-07-12
  • 2021-11-15
  • 1970-01-01
  • 1970-01-01
  • 2021-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多