Reputation: 23
I have a posts collection in firestore from which I am getting all posts created by users in a list [user1, user2, ...]. I can listen to changes in collection using onSnapshot in the method below:
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>
But this 'in' query wont work if the friendList has more than 10 elements. So I can get the required documents from the collction using promises like:
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);
});
});
}
But I no longer can listen to the changes in database to update my react state to render accordingly if a post was added or deleted in my firestore collection. How can I setup my useEffect for the promises solution in the second code such that I can update my react state everytime there is change in the firestore collection? Thank you.
Upvotes: 1
Views: 3249
Reputation: 1476
Use the onSnapshot
in useEffect
block. Simple example;
useEffect(() => {
onSnapshot(collection(db, "profiles"), (snapshot) => {
setProfiles(snapshot.docs.map((doc)=>({...doc.data(), id: doc.id})))
})
console.log(profiles);
});
The profiles
state will contain updated items all the time.
Upvotes: 1
Reputation: 7388
You would need to leave the listeners open and update the state each time they are triggered and merge the data according to the post id
s like here:
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"));
});
}
}
Upvotes: 1