【问题标题】:Can't render a collection of data successfully fetched from firebase with react hook无法使用反应挂钩呈现从 Firebase 成功获取的数据集合
【发布时间】:2020-05-24 05:44:12
【问题描述】:

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

    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()
    }, [])

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

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

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

【问题讨论】:

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


    【解决方案1】:

    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 的最佳理由是你不能保证 last "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>
    

    【讨论】:

      【解决方案2】:

      将 key prop 添加到 Link 组件。

      示例

      const fetchPosts = () => {
          firebase
            .firestore()
            .collection("posts")
            .get()
            .then(snap => {
              let docs = [];
              snap.docs.forEach(doc => {
                  docs.push(doc.data());
              });
              setPosts( prevState => [...prevState, ...docs]);
            });
        };
      
      <div className="posts section">
          {posts.map(post => {
          return (
              <Link key={post.id} to={"/posts/" + post.id}>
               <PostCard post={post} />
              </Link>
          );
          })}
      </div>
      

      希望这能解决问题。

      【讨论】:

      • 不,它没有......同样的问题。仍然只渲染第一个获取的文档。
      • 你能创建一个沙箱并在那里添加你的代码吗?
      • 在链接中添加键删除了控制台错误..但对渲染没有影响。奇怪的是所有获取的帖子都是控制台日志记录好......但它们没有被反应呈现
      • 试试更新的代码。使用回调来更新状态。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-26
      • 2020-04-12
      • 1970-01-01
      相关资源
      最近更新 更多