Angelo
Angelo

Reputation: 1666

Proper Way To Subscribe for Real-Time Changes on Firestore with React

I've just started dipping some knowledge into using Firestore with React. I am looking to gain more insight on the real-time database capabilities offered by Firestore.

The following are my code snippets in each corresponding file:

UserComponent.js

import React, { Component } from 'react'

class UserPage extends Component {
  constructor (props) {
    super (props)
    this.state = {
      users: []
    }
  }

  componentDidMount () {
    this.props.firebase.getUsers()
    .then(doc => {
      console.log('componentDidMount', 'Data Received')
      this.setState({ users: doc })
    }
  }

  render () {
    const { users } = this.state
    return (
      <div>
        <h1>Users</h1>
        <hr />
        <table>
          <thead></thead>
          <tbody>
            { users.map (user => (
                <tr key={user.uid}>
                  <td>{ user.uid }</td>
                  <td>{ user.name }</td>
                </tr>
              ))
            }
          </tbody>
        </table>
      </div>
    )
  }
}

Firebase.js

import app from 'firebase/app'
import 'firebase/auth'
import 'firebase/database'
import 'firebase/firestore'

const config = {
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_APP_AUTH_DOMAIN,
  databaseURL: process.env.REACT_APP_DATABASE_URL,
  projectId: process.env.REACT_APP_PROJECT_ID,
  storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID 
}

class Firebase {
  constructor (props) {
    app.initializeApp(config)
    this.auth = app.auth()
    this.db = app.database()
    this.firestore = app.firestore()
  }

  getUsers = () => {
    return new Promise((resolve, reject) => {
      this.firestore.collection("users")
      .onSnapshot((snapshot) => {
        console.log('onSnapshot Called!')
        let updatedData = snapshot.docs.map(doc => doc.data())
        resolve(updatedData)
      }, reject)
    })
  }
}

When I go directly to the firestore database, modify an existing user record, the console.log I have placed in the getUsers method right after the onSnapShot is triggered. The snapshot also contains the update set of users record on it.

I may be missing something here (or implemented things incorrectly) as that data never reaches back the User component. I seek some guidance on making sure that when the users collection on my firestore database has changed, the listener is triggered, and the updated data will be reflected on my application's user component.

The other question I have is that, in the original Firebase RealTime Database, there is a method called off, which is used to unsubscribe for changes. What would be the counterpart of this in Firestore? I assume that this needs to be placed inside the ComponentWillUnmount of the React component?

Thanks,

Upvotes: 0

Views: 5114

Answers (1)

Methkal Khalawi
Methkal Khalawi

Reputation: 2477

your Firebase.js should:

import app from 'firebase/app'
import 'firebase/auth'
import 'firebase/database'
import 'firebase/firestore'

const config = {
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_APP_AUTH_DOMAIN,
  databaseURL: process.env.REACT_APP_DATABASE_URL,
  projectId: process.env.REACT_APP_PROJECT_ID,
  storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID 
}



const firebase = app.initializeApp(config)
const auth = app.auth()
const db = app.database()
const firestore = app.firestore()

export const getUsers = () => {
    return new Promise((resolve, reject) => {
      firestore.collection("users")
      .onSnapshot((snapshot) => {
        console.log('onSnapshot Called!')
        let updatedData = snapshot.docs.map(doc => doc.data())
        resolve(updatedData)
      }, reject)
    })
  }

your UserComponent.js should be:

import React, { Component } from 'react';
import {getUsers} from './firebase';

class UserPage extends Component {
  constructor (props) {
    super(props);
    this.state = {
      users: [],
    }
  }

  componentDidMount(){
    getUsers().then(doc => {
      console.log('componentDidMount', 'Data Received');
      this.setState({ users: doc });
    })
  }
  render(){
    const { users } = this.state
    return (
      <div>
        <h1>Users</h1>
        <hr />
        <table>
          <thead></thead>
          <tbody>
            { users.map (user => (
                <tr key={user.uid}>
                  <td>{ user.uid }</td>
                  <td>{ user.name }</td>
                </tr>
              ))
            }
          </tbody>
        </table>
      </div>
    )
  }
}

export default App;

You should also import UserPage component to your App.js and add it their to be rendered in the App or you can render UserPage directly in the primary index.js file.

Upvotes: 3

Related Questions