0

I am trying to retrieve 2 queries and do a calculation between the two. But it is rather slow or returning undefined.

Sometimes it shows me the values, but that is usually after I refresh the app.

The first where, gives the amount of documents where field 'active' is true. Then the second part is checking the active and hasread fields and returns the amount.

This is the code I am using:

export default class Home extends React.Component {
    constructor(props) {
        super(props)
        this.gettingAmount = false;
        this.unsubscribe = null;
        this.announce = firebase.firestore().collection('announcements');
        this.state = {
            newAnnouncements: 0,
        }
    }
    componentDidMount() {
        this.gettingAmount = true;
        let countHasRead;
        let countAnnounce;
        this.unsubscribe = firebase.auth().onAuthStateChanged((user) => {
            if (user) {
                this.announce
                    .where('active', '==', true)
                    .get()
                    .then(snapshot => {
                        countAnnounce = snapshot.size;
                    });
                this.announce
                    .where('active', '==', true)
                    .where('hasread.' + user.uid, '==', true)
                    .get()
                    .then(snapshot => {
                        countHasRead = snapshot.size;
                    })
                    .catch(err => {
                        console.log('Error getting documents', err);
                    });
                setTimeout(() => {
                    console.log('second', countAnnounce, countHasRead);
                    if (this.gettingAmount) {
                        this.gettingAmount = false;
                        this.setState({newAnnouncements: countAnnounce - countHasRead});
                        AsyncStorage.setItem('newAnnouncements', JSON.stringify(countAnnounce - countHasRead));
                    }
                }, 1000);
            }
        });
    }
}

So the console.log('second') shows either undefined or the query is really slow and does show the values of countAnnounce and countHasRead.

Is it something I am doing wrong? I am unsure why it is showing as undefined.

Please help.

udarts
  • 745
  • 1
  • 8
  • 23

1 Answers1

1

The problem is not so much that the query is slow, as much that it is asynchronous.

A quick way to see what is happening is with a few log statements:

console.log("Before starting query");
this.announce
    .where('active', '==', true)
    .get()
    .then(snapshot => {
        console.log("Got query results")
    });
console.log("After starting query")

If you run this code, it prints:

Before starting query

After starting query

Got query results

This is probably not the order you expected, but it is exactly what is supposed to happen. Because loading data from Firestore may take some time, the operation happens in the background while the rest of your code continues. Then when the data is loaded, it calls your callback function with that data, so you can process it.

This means that any code that needs access to the data from the database must be (called from) inside the callback function.

So in you case, you'll need to nest the loading:

if (user) {
    this.announce
        .where('active', '==', true)
        .get()
        .then(snapshot => {
            countAnnounce = snapshot.size;
            this.announce
                .where('active', '==', true)
                .where('hasread.' + user.uid, '==', true)
                .get()
                .then(snapshot => {
                    countHasRead = snapshot.size;
                    console.log('second', countAnnounce, countHasRead);
                    if (this.gettingAmount) {
                        this.gettingAmount = false;
                        this.setState({newAnnouncements: countAnnounce - countHasRead});
                        AsyncStorage.setItem('newAnnouncements', JSON.stringify(countAnnounce - countHasRead));
                    }
                })
                .catch(err => {
                    console.log('Error getting documents', err);
                });
        });

}

Unrelated to the problem, I recommend reading Better Arrays in Cloud Firestore! because there is now a more efficient way to do this: .where('hasread.' + user.uid, '==', true) that requires far fewer indexes.

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks Frank, I was looking to the option of using: `.where('userhasread', 'array-contains', userId)`, using it with react-native-firebase, but this is not working for me, already filled a issue about it. – udarts Jan 09 '19 at 15:40
  • Good to hear. The rest of my answer should explain the rest if your problem. – Frank van Puffelen Jan 09 '19 at 15:56
  • I was wondering if when using the arrayUnion to add a user.uid to an array, if it should should in Firestore like: `hasRead: [0: user.uid]` or as: `hasRead: [user.uid]`. Currently it shows as the first. – udarts Jan 10 '19 at 07:39
  • You should store them in the latter form for the new more efficient querying, that's what the last comment in my answer points out. – Frank van Puffelen Jan 10 '19 at 14:16
  • Hi Frank, I tried to store them in the same way as was mentioned in the link you posted. According to that link, only the user id should be stored in the array, but as I showed, it stores it as: `0: user.id`. I accepted your answer, but would like some help with that storing issue. – udarts Jan 10 '19 at 15:24
  • I'd recommend opening a new question for that, so that you can limit to just the minimum that is needed to reproduce *that* issue. Be sure to include how the data is stored/shown in the Firebase console (screenshots typically work best there), and the code for how you write it. – Frank van Puffelen Jan 10 '19 at 15:28
  • Thanks @Frank, will do. – udarts Jan 13 '19 at 15:35