我正在从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()
}, [])
我还将此状态传递给其他组件,以便它们也使用更新的状态重新渲染
但是 react 只是渲染了收集的第一个文档,并在控制台中给出了错误:"每个孩子都应该有一个唯一的键道具"。我的每个文档对象内部都有一个唯一的ID,我将其作为每个帖子的键传递
<div className="posts section">
{posts.map(post=>{
return <Link to={'/posts/'+post.id}><PostCard post={post} key={post.id} /></Link>
})}
</div>
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 为异步。 我使用以下方法来处理查询快照:
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>
将键道具添加到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>
希望这将解决问题。