首页 > 解决方案 > REACT + FIRESTORE :监听 react 组件中 firebase 集合的变化以自动更新反应状态

问题描述

我在firestore中有一个帖子集合,我从中获取列表[user1,user2,...]中用户创建的所有帖子。我可以在下面的方法中使用 onSnapshot 来监听集合的变化:

export default function Feed({ friendList }) {
  // currentUser context
  const { currentUser } = useAuth();

  // array to save posts from database
  const [posts, setPosts] = useState([]);

  // function to fetch data from database
  useEffect(() => {

    db.collection('posts')
      .where('userId', 'in', [...friendList])
      .orderBy('timestamp', 'desc')
      .onSnapshot((snapshot) => {
        setPosts(
          snapshot.docs.map((doc) => ({
            id: doc.id,
            post: doc.data(),
          }))
        );
      });
 }, []);

  return (
    <div className='feed'>
      {currentUser ? (
        <div className='feed_container'>
          {posts.map(({ id, post }) => {
            return (
              <Post
                key={id}
                id={id}
                userProfileURL={post.userProfileURL}
                username={post.username}
                displayName={post.displayName}
                photoURL={post.photoURL}
                />
            );
        })}
      </div>

但是如果friendList 有超过10 个元素,这个'in' 查询将不起作用。因此,我可以使用以下承诺从收集中获取所需的文件:

useEffect(() => {
    getPostsFromFriendsList(friendList);
}, []);

async function getPostsFromFriendsList(friendList){
    const promises = [];
    for (let i = 0; i < friendList.length; i++) {
      promises.push(getPosts(friendList[i]));
    }

    const postsForHomeFeed = await Promise.all(promises);
    const array = [];
   
    for (let i = 0; i < postsForHomeFeed.length; i++) {
      postsForHomeFeed[i].docs.map((doc) => {
        array.push({
          id: doc.id,
          post: doc.data(),
        });
      });
    }
   setPosts(array);
}
  function getPosts(userId) {
    return new Promise((resolve, reject) => {
      db.collection('posts')
        .where('userId', 'in', [userId])
        .onSnapshot((snapshot) => {
          resolve(snapshot);
        });
    });
  }

但是,如果在我的 firestore 集合中添加或删除了帖子,我不再可以监听数据库中的更改来更新我的反应状态以相应地呈现。如何在第二个代码中为 Promise 解决方案设置我的 useEffect 以便我可以在每次 firestore 集合发生变化时更新我的​​反应状态?谢谢你。

标签: javascriptreactjsfirebasegoogle-cloud-firestoreuse-effect

解决方案


您需要让监听器保持打开状态,并在每次触发它们时更新状态,并根据 post 合并数据,id如下所示:

useEffect(() => {
  getPostsFromFriendsList(friendList);
}, []);

//source: https://stackoverflow.com/questions/7146217/merge-2-arrays-of-objects
const merge = (a, b, prop) => {
  var reduced = a.filter(
    (aitem) => !b.find((bitem) => aitem[prop] === bitem[prop])
  );
  return reduced.concat(b);
};

async function getPostsFromFriendsList(friendList) {
  const promises = [];
  for (let i = 0; i < friendList.length; i++) {
    db.collection("posts")
      .where("userId", "in", [friendList[i]])
      .onSnapshot((snapshot) => {
        const array = [];
        snapshot.docs.map((doc) => {
          array.push({
            id: doc.id,
            post: doc.data(),
          });
        });

        setPosts(merge([...posts], array, "id"));
      });
  }
}

推荐阅读