user13290052
user13290052

Reputation:

How does a Firestore onSnapshot listens for changes in React-Native?

I have seen in multiple places people placing the listener code inside the componentDidMount, however, I am wondering how does it work since componentDidMount is called only once, therefor, it should fetch only once. How does it fetches again when changes happen in Firestore?

Example code :

componentDidMount() {
  if(this.state.screen === 7){
        var query = firestore().collection('Collection').doc().collection('subcollection');
            query = query.where('act', '==', 1);
            query = query.where('city', '==', this.state.selected_city);
            query = query.orderBy('update_time', 'desc');
            query = query.limit(10);

            query.onSnapshot({
                error: (e) => this.setState({ errorMessage: e, refreshingPatients: false }),
                next: (querySnapshot) => {
                    var dataSource = querySnapshot.docs.map(doc => { return { ...doc.data(), doc_id: doc.id } });                       
                    var lastVisiblePatient = dataSource[dataSource.length - 1].doc_id;
                    this.setState({
                        dataSource: dataSource,
                        lastVisiblePatient: lastVisiblePatient,
                        refreshingPatients: false,
                    });
                },
            });
    }
 }

EDIT: added an example code. I want to listen to changes of Firestore DB all the time. Is that the correct way of doing it? If yes, how does it work because componentDidMount is only called once? Note: I am only listening to Firestore when screen state is set to 7.

Upvotes: 1

Views: 2648

Answers (2)

Kid
Kid

Reputation: 1240

Solution1: Read the comment inside the code.

componentDidUpdate(prevState, prevProps) {
  if(prevState !== this.state.screen && this.state.screen === 7) {

        var query = firestore().collection('Collection').doc().collection('subcollection');
            query = query.where('act', '==', 1);
            query = query.where('city', '==', this.state.selected_city);
            query = query.orderBy('update_time', 'desc');
            query = query.limit(10);

            query.onSnapshot({
                error: (e) => this.setState({ errorMessage: e, refreshingPatients: false }),
                next: (querySnapshot) => {
                    var dataSource = querySnapshot.docs.map(doc => { return { ...doc.data(), doc_id: doc.id } });                       
                    var lastVisiblePatient = dataSource[dataSource.length - 1].doc_id;
                    this.setState({

                    // you are not updating screen so it is always 7
                    // if you update other state variables, it will trigger ComponentDidUpdate again, 
                    // and again and again and again

                        dataSource: dataSource,
                        lastVisiblePatient: lastVisiblePatient,
                        refreshingPatients: false,
                    });
                },
            });
    }
 }

By adding prevState !== this.state.screen, you can prevent componentDidUpdate being triggered everytime you update other states.

Like dataSource, lastVisiblePatient, and refreshingPatients.

Solution 2: Since you are using class components, you can use setState callback.

this.setState({screen: newValue}, doSomethingNew);

doSomethingNew(){
     if (this.state.screen === 7) {
         // your code goes here
     }
}

the callback function(here doSomethingNew) is triggered after screen value is updated with newValue.

Solution3: If you convert to function component, you can use useEffect dependency.

const [screen, setScreen] = useState(initialValue);    
useEffect(() => {
    if (screen === 7) {
        //do something here
    }
}, [screen]);    // adding screen as a dependency means this  useEffect is triggered only when screen value is changed.

Upvotes: 0

Frank van Puffelen
Frank van Puffelen

Reputation: 599776

Your componentDidMount is indeed only called once, but you're attaching a permanent listener in there. So the handler that you specify in onSnapshot.next is called right away with the initial data from the database, and after that each time that the data matching the query changes.

Upvotes: 1

Related Questions