如何在进行之前等待获取 Firebase 数据?

How to wait for Firebase data to be fetched before progressing?

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

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

我当前的代码是:

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;

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

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

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>}
    </>
  );
}

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

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

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;