score:2

get() returns a promise, which is used for asynchronous code.

the then() method returns a promise. it takes up to two arguments: callback functions for the success and failure cases of the promise.

therefore the console.log(posts); outside the then method will return undefined since the data is not retrieved yet. you need to do the following:

const getposts = () => {
    const posts = [];
    firestore.collection(`posts`).get().then((snapshot) => {
        snapshot.docs.foreach((doc) => {
            let data = { ...doc.data() };
            posts.unshift(data);
            console.log(posts);    
            console.log(posts[1]);

        });
    });
};

if you want to access the array outside the then() method then you can return a promise:

const getposts = () => {
    const posts = [];
  return new promise((resolve, reject) => {  
  firestore.collection(`posts`).get().then((snapshot) => {
        snapshot.docs.foreach((doc) => {
            let data = { ...doc.data() };
            posts.unshift(data);
            console.log(posts); 
            resolve(posts);  
            console.log(posts[1]);

        });
    });
   });
};

then you can do the following:

getposts().then((posts) => {
   console.log(posts);
 });

check the following links:

https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/promise

https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/promise/then

score:2

i would recommend in general to avoid mutating the array using push. that said, you could return the posts from the function so it is easier to work with them

const getposts = async () => {
    const snapshot = await firestore.collection('posts').get();
    return snapshot.docs.map(snap => object.assign({}, { ...snap.data() }))
}

the await keyword will delay the execution of the code that depends on "snapshot" until the promise is resolved.

edit i put it in its original form again as it is concise and readable. the problem is that the getpost func should be awaited too, because if you do something like:

const posts = getposts()

it would be used immediately and not wait for the getpost() to finish. you can use a .then if you want in getposts, e.g:

getposts().then(result => console.log(result))

you could also wrap the function that uses the getposts() in an async/await and be able to use its results as it were a sync function.

refer here for a related question and another explanation, and also fo the other answers in this same question.

score:3

you have introduced asynchronous code here.

firestore.collection(`posts`).get().then((snapshot) => {
        snapshot.docs.foreach((doc) => {
            let data = { ...doc.data() };
            posts.unshift(data);
        });
    });

the get() method returns a promise, which will be handled inside .then() method once it gets resolved. that means, only the contents inside the then() will wait for its execution to finish and any thing outside of the block will be executed synchronously.

in your case, all console.log() methods will get executed before you actually get data from the fire base. to fix it you can either manipulate the data inside the then() method like peter said or you can make use of async/await.

example:

var promise1 = new promise(function(resolve, reject) {
  settimeout(function() {
    resolve('foo');
  }, 300);
});

const consoleit = async () => { // returns a promise implicitly
  value = await promise1;
  console.log('value',value)
}
consoleit(); // expected output: "value" "foo"

//or

const consoleit2 = async () => { // returns a promise implicitly
  value = await promise1;
  return value;
}
consoleit2().then(value => console.log('value', value)); // expected output: "value" "foo"

your code should be:

const getposts = async () => {
    const posts = [];
    let snapshot = await firestore.collection(`posts`).get(); // rest of the code will wait for the snapshot value.
    snapshot.docs.foreach((doc) => {
            let data = { ...doc.data() };
            posts.unshift(data);
        });
    console.log(posts);  
    console.log(posts.length);  
    console.log(posts[1]);  // will print the object.
    return posts;
};

async functions usually returns promise. you can call this function like

const posts = await getposts(); or getposts().then((posts)=> {console.log(posts)})

your code should be like this

const postslist = () => {
getposts().then((posts) => {
 console.log(posts); // prints object
 return posts.map((post) => <postitem key={post.id} event={post} />); };
}); 

// or if you want to handle no data.
let posts = null;
const postslist = () => {
getposts().then((res) => {
post = res;
 console.log(posts); // prints object
});
return posts? posts.map((post) => <postitem key={post.id} event={post} />) : <nodata/>; 
}

note : await keyword should only be used inside an async function!


Related Query

More Query from same tag