Reputation: 4253
I have this reactjs function to get data from firebase. The problem is the postsArray. It inside foreach has the objects, outside it is null.
I add postsArray globally.
Any ideas why the return is null?
postsRef.on('value', function(snapshot) {
setPosts(prevPosts => {
snapshot.forEach((subChild) => {
var value = subChild.val();
value = value.postID;
var post = firebase.database().ref('/posts/' + value);
post.on('value', function(snapshot2) {
postsArray = snapshot2.val();
console.log(postsArray); // HAS THE VALUE
});
console.log(postsArray); // NO VALUE HERE.
});
return [...prevPosts, ...Object.keys(postsArray).reverse().map(key => ({
key: key, ...postsArray[key]
}))];
Upvotes: 1
Views: 396
Reputation: 94
post.on()
is an asynchronous event listener, your current code is synchronous. If you post more of your code I can help you restructure it.
What is happening is:
var post = firebase.database().ref('/posts/' + value);
var postFunction = function(snapshot2) {
postsArray = snapshot2.val();
console.log(postsArray); // HAS THE VALUE
};
// set the post on function. This isn't calling the function yet.
post.on('value', postFunction);
// No value here because the post function has not run yet.
console.log(postsArray); // NO VALUE HERE.
// the postFunction post.on is called so now you will get the console.
I would change it to look like this:
const addNewPostsToPrevious = (prevPosts) => (newPosts) => {
return [
...prevPosts,
...Object.keys(newPosts)
.reverse()
.map((key) => ({
key,
...newPosts[key],
})),
];
};
postsRef.on('value', (snapshot) => {
setPosts((prevPosts) => {
snapshot.forEach((subChild) => {
const post = firebase.database().ref(`/posts/${subChild.val().postID}`);
post.on('value', (snapshot2) => {
addNewPostsToPrevious(prevPosts)(snapshot2.val());
});
});
});
});
Upvotes: 1
Reputation: 12232
post.on
is async function that is why the value outside of the forEach loop is undefined for postArray
The solution here is to set state inside post.on. This however will lead to multiple setState and also keep adding the postArray data state.
postsRef.on('value', function(snapshot) {
snapshot.forEach((subChild) => {
var value = subChild.val();
value = value.postID;
var post = firebase.database().ref('/posts/' + value);
post.on('value', function(snapshot2) {
postsArray = snapshot2.val();
setPosts(prevPosts => {
return [...prevPosts, ...Object.keys(postsArray).reverse().map(key => ({
key: key, ...postsArray[key]
}))];
})
});
})
})
A better solution here is to convert your callback syntax to use promise. I am not sure if firebase.database provides a promise format so I will just show you the traditional way using Promise.all and new Promise
postsRef.on('value', async function(snapshot) {
const promises = []
snapshot.forEach((subChild) => {
var value = subChild.val();
value = value.postID;
var post = firebase.database().ref('/posts/' + value);
promises.push(new Promise((res, rej) => {
post.on('value', function(snapshot2) {
res(snapshot2.val())
});
}));
})
const postArray = await Promise.all(promises);
setPosts(prevPosts => {
return [
...prevPosts,
...Object.keys(postsArray).reverse().map(key => ({
key: key, ...postsArray[key]
}))
];
})
})
Upvotes: 1