1

Inside a function, I first create an array of the current state, to use in a for loop. However when trying to get it's length, it returns 0. Logging both the array from the state and the new array shows that the array does include the right objects, and even shows the length as the proper amount.

console.log(this.state.teams);
console.log(this.state.teams.length);
let sortedTeams = this.state.teams;
console.log(sortedTeams);
console.log(sortedTeams.length);

Showing Console Output

What could be causing the length to be returned as 0, even though it's clearly not?? The array is being mapped from firestore as follows:

getTeamsAndSort() {
    let teams = [];
    docRef.collection('Teams').get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            let name = doc.data().name;
            let seeding = doc.data().seeding;
            let team = {name, seeding};
            teams.push(team);
        });
        teams.sort((a, b) => (a.seeding > b.seeding) ? 1 : -1);
    });
    this.setState({teams});
};

The function where I need the length of teams[] is called immediately after I call getTeamsandSort(), in case it is an asynchronous issue.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
Luke Egan
  • 81
  • 6

2 Answers2

1

What could be causing the length to be returned as 0, even though it's clearly not??

This is because when you logged it, the length was 0. However, if you hover over the "i" icon (in Chrome inspect console) you will see a message that says objects are updated in the console as the object changes.

You can prevent the misleading logs with:

console.log(JSON.parse(JSON.stringify(sortedTeams)));

A possible solution to your problem:

getTeamsAndSort = () => {
    let teams = [];
    docRef.collection('Teams').get()
    .then(querySnapshot => {
        querySnapshot.forEach(function(doc) {
            let name = doc.data().name;
            let seeding = doc.data().seeding;
            let team = {name, seeding};
            teams.push(team);
        });
        teams.sort((a, b) => (a.seeding > b.seeding) ? 1 : -1);
        return new Promise((resolve, reject) => {
            // resolve the promise after state is set
            this.setState({teams}, resolve);
        })
    });
};

Then, if you need to use this.state.teams immediately after calling getTeamsAndSort, await the Promise to make sure that the state was updated before logging it or accessing its properties.

async someFunc() {
    await getTeamsAndSort()
    console.log(JSON.parse(JSON.stringify(sortedTeams)));
}
twharmon
  • 4,153
  • 5
  • 22
  • 48
0

docRef.collection('Teams').get() is asynchronous and returns immediately, before the query is complete. In fact, all functions that return promises will behave the same way. Your code must only use the result of the query after the promise resolves in the then block.

Your code is calling this.setState({teams}); with an empty teams array before the results of the database are complete. You should only set the state inside the callback:

getTeamsAndSort() {
    let teams = [];
    docRef.collection('Teams').get()
    .then(querySnapshot => {
        querySnapshot.forEach(function(doc) {
            let name = doc.data().name;
            let seeding = doc.data().seeding;
            let team = {name, seeding};
            teams.push(team);
        });
        teams.sort((a, b) => (a.seeding > b.seeding) ? 1 : -1);
        // only set the state after the teams are available
        this.setState({teams});
    });
};

Also bear in mind that you will not be able to access the length of teams immediately after calling getTeamsAndSort because the whole thing is now asynchronous. Bottom line is that you will have to learn how to deal with promises effectively to work with database queries like this.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • I tried this before posting, but inside that bracket, the compiler throws an error saying "this is undefined", even though I bound this to that function. Any ideas? – Luke Egan Apr 10 '20 at 16:32
  • I edited the answer to use the => syntax for lambda functions, which should preserve `this`. – Doug Stevenson Apr 10 '20 at 16:40
  • Thank you this worked vary nicely, promises are definitely something I need to look into as I go on. – Luke Egan Apr 10 '20 at 16:59