Alex Foxleigh
Alex Foxleigh

Reputation: 1974

React components not updating when firebase updates

I've set up a people tracker for my smart home dashboard (built in react and firebase). However when the firebase database changes, it is not causing a redraw on the react component. I can't tell where I am going wrong, could someone please help me?

import React from 'react';
import * as firebase from 'firebase';
import moment from 'moment';

export default class IO extends React.Component {
  constructor() {
    super();
    this.state = {
      people: []
    };
  }

  componentDidMount() {
    let peopleArray = []
    this.dbroot = firebase.database().ref().child('io')
    this.dbroot.on('value', snapshot => {
      snapshot.forEach((snap) => {
        if(snap.val().active === true) {
          peopleArray.push(snap.val())
        }
      })
      this.setState({people: peopleArray})
    });
  }

  render() {
    return(
      <section class="c-module c-module_IO">
        <ul>
          {
            this.state.people.map((p) => {
              return <li key={ p.name } class={'status--'+p.status}><img src={p.image} alt={ p.name } /></li>
            })
          }
        </ul>
      </section>
    )
  }
}

Upvotes: 4

Views: 3654

Answers (1)

Joey Grisafe
Joey Grisafe

Reputation: 415

When your component first mounts, it calls ComponentDidMount and initializes the peopleArray. This is only done once and thus this array is being mutated every time you receive updates from Firebase. With your current implementation, you are mutating the state as the reference to peopleArray is being held in state as a pointer, and not by value. React does not do deep comparisons when it checks to see if something has changed in the state tree, it does object comparisons. This is why you never want to mutate your state, always create new objects when setting state. This goes for redux as well.

A super quick fix would be to move the line let peopleArray = [] into the firebase onValue handler, so that a new array is created every time you get an update. Also, with the current implementation you are going to keep appending your Firebase values onto the same array, so you may get duplicates. The fix I mentioned will take care of that as well.

Here is your final CDM:

componentDidMount() {
  this.dbroot = firebase.database().ref().child('io')
  this.dbroot.on('value', snapshot => {
    let peopleArray = []
    snapshot.forEach((snap) => {
      if(snap.val().active === true) {
        peopleArray.push(snap.val())
      }
    })
    this.setState({people: peopleArray})
  });
}

Upvotes: 5

Related Questions