首页 > 解决方案 > 无法使用反应挂钩呈现从 Firebase 成功获取的数据集合

问题描述

我正在从 firebase 获取一个文档集合,然后将其传递给一个状态,以便在更新时重新渲染它。

    const [posts, setPosts] = useState([])

    const fetchPosts = () => {
        firebase.firestore().collection('posts').get()
            .then(snap => {
                snap.docs.forEach(doc => {
                    setPosts([...posts, doc.data()])
                    console.log(doc.data())
                })
            })
    }



    useEffect(() => {
        fetchPosts()
    }, [])

我还将这个状态传递给其他组件,因此它们也使用更新的状态重新渲染

但是反应只是渲染第一个集合文档并在控制台中给出错误:'每个孩子都应该有一个唯一的关键道具'。我的每个 doc 对象内部都有一个唯一的 id,我将其作为每个帖子的键传递

    <div className="posts section">
        {posts.map(post=>{
            return <Link to={'/posts/'+post.id}><PostCard  post={post} key={post.id} /></Link>
        })}
    </div>

标签: reactjsfirebasegoogle-cloud-firestorereact-hooks

解决方案


Google 不建议使用文档/数组数据作为键,因为后续渲染可能效率低下。一个可爱的 React 函数可以解决独特的关键问题。

<div className="posts section">
    {React.Children.toArray(
      posts.map(post => {
        return (
          <Link to={"/posts/" + post.id}>
           <PostCard post={post} />
          </Link>
        );
      })
    )}
</div>

您可能遇到的另一个问题是 useEffect 必须是同步的。您可能希望将 fetchPosts 显式声明为异步。我使用以下内容来处理 querySnapshot:

  return query
    .get() //get the resulting filtered query results
    .then(querySnapshot => {
      return Promise.resolve(
        querySnapshot.docs.map(doc => {
          return {
            ...doc.data(),
            Id: doc.id,
            ref: doc.ref
          };
        })
      );
    })

.map 的最佳原因是您不能保证最后一个“setPosts”在您的下一个循环之前实际上已经完成,因此您的状态(在这种情况下是“posts”)可能是陈旧的。

所以,除此之外,我的模式是:

   const [posts, setPosts] = useState([])

    const fetchPosts = () => {
        return firebase.firestore().collection('posts').get()
        .then(snap => {
            snap.docs.map(doc => {
                console.log(doc.data())
                return {
                   ...doc.data(),
                   id: doc.id,
                   ref: doc.ref
                };
            })
        });
    }

    useEffect(() => {
        (async () => {
            const newPosts = await fetchPosts();
            setPosts(newPosts);
        })();
    }, [])

    //[etc, etc]

    return
    //[etc, etc]

        <div className="posts section">
        {React.Children.toArray(
            posts.map(post=>{
                return <Link to={'/posts/'+post.id}><PostCard  post={post} key={post.id} /></Link>
            })
        }
        </div>

推荐阅读