Displaying Content from Firebase 

In notebook:
FrontEndMasters Serverless
Created at:
2017-10-12
Updated:
2017-10-16
Tags:
backend JavaScript libraries React

Now, we want to display the list of restaurants on the screen. We want to subscribe to the restaurants database and update the app state. In React, the common pattern is to push your data to the "top" and pass it down. This can become tricky as the application grows. He mentions Redux as a solution to this.

We want to subscribe to the /restaurants ref in the database.

  //	****		Application.js		****

import React, { Component } from 'react';
import { auth, database } from './firebase';
import CurrentUser from './CurrentUser';
import SignIn from './SignIn'; 
import NewRestaurant from './NewRestaurant';
import Restaurants from './Restaurants';
import './Application.css';

// **** 3 use lodash for object iteration.  ↴
import map from 'loadash/map'

class Application extends Component {
  constructor(props) {
    super(props);
    this.state = {
      currentUser: null,
      // **** 5.  ↴
      restaurants: null
    }
    
    // **** 1.  get a reference to the database  ↴
    this.restaurantRef = database.ref('/restaurants')
  }
  
  componentDidMount() {
    auth.onAuthStateChanged((currentUser) => {
      this.setState({ currentUser })
      // **** 2 only listen to the db once the user has logged in.  ↴
      this.restaurantRef.on('value', snapshot => {
        // we need to use the snapshot of the database as there can be many mutations happening the same time
        console.log(snapshot.val()) // first just log out
        // **** 4.  ↴
        this.setState({ restaurants: snapshot.val() })
        // at this point the react state updates on load
      })
    })
  }

  render() {
    // **** 6 grab also `restaurants`.  ↴
    const { currentUser, restaurants } = this.state
    
    return (
      <div className="Application">
        <header className="Application--header">
          <h1>Lunch Rush</h1>
        </header>
        <div>
          {!currentUser && <SignIn />}
          {currentUser && 
          <div>
            <NewRestaurant />
            // **** 7.  map with lodash ↴
            { map(restaurants, (restaurant, key) => <p key={key}>{ restaurant.name }</p>)} // `key`: so that React knows which smallest node to change in the DOM
            <CurrentUser user={currentUser} />
            // now the circle is closed, adding and displaying a new restaurant through Firebase is working
          </div>
          } 
        </div>
      </div>
    );
  }
}

export default Application;

Note that above, Firebase is not using arrays for the restaurants, but object:key pairs. So we need to iterate over it like an array. It's a bit weird to do this with JavaScript (own properties, ES6 tricks...). Suggests lodash to do this.

Adding voting

  //	****		Application.js		****

import React, { Component } from 'react';
import { auth, database } from './firebase';
import CurrentUser from './CurrentUser';
import SignIn from './SignIn'; 
import NewRestaurant from './NewRestaurant';
import Restaurants from './Restaurants';
import './Application.css';

import map from 'loadash/map'

class Application extends Component {
  constructor(props) {
    super(props);
    this.state = {
      currentUser: null,
      restaurants: null
    }
    
    this.restaurantRef = database.ref('/restaurants')
  }
  
  componentDidMount() {
    auth.onAuthStateChanged((currentUser) => {
      this.setState({ currentUser })
      this.restaurantRef.on('value', snapshot => {
        this.setState({ restaurants: snapshot.val() })
      })
    })
  }

  render() {
    const { currentUser, restaurants } = this.state
    
    return (
      <div className="Application">
        <header className="Application--header">
          <h1>Lunch Rush</h1>
        </header>
        <div>
          {!currentUser && <SignIn />}
          {currentUser && 
          <div>
            <NewRestaurant />
            // **** 1 ++-- pass the restaurants data to the component.  ↴
            // { map(restaurants, (restaurant, key) => <p key={key}>{ restaurant.name }</p>)}
            <Restaurants restaurants={restaurants} />
            <CurrentUser user={currentUser} />
          </div>
          } 
        </div>
      </div>
    );
  }
}

export default Application;

Moving on to the Restaurants.js

  import React, { Component, PropTypes } from 'react';
import Restaurant from './Restaurant';
import map from 'lodash/map';
import './Restaurants.css';

class Restaurants extends Component {
  constructor(props) {
    super(props);
  }

  render () {
    // **** 1.  ↴
    const { restaurants } = this.props
    return (
      <section className="Restaurants">
        // **** 2. map over the restaurants here (push downwards the data)  ↴
        // since the `restaurant` data _will_ grow over time
        // from just having the `name` property, will use the `...` spread operator
        // this will take each key of the object and pass them individually
        { map(restaurants, (restaurant, key) => <Restaurant key={key} {...restaurant} />))
      
      </section>
    );
  }
}

Restaurants.propTypes = {
  user: PropTypes,
  restaurantsRef: PropTypes.object,
  restaurants: PropTypes.object
};

export default Restaurants;

Now, continue with displaying a single restaurant (subcomponent)

  //	****		Restaurant.js		****

import React, { Component, PropTypes } from 'react';
import map from 'lodash/map';
import './Restaurant.css';

class Restaurant extends Component {
  render () {
    // **** 1.  ↴
    const { name } = this.props
    return (
      <article className="Restaurant">
      // **** 2.  ↴
        <h3> { name } </h3>
      </article>
    );
  }
}

Restaurant.propTypes = {
  name: PropTypes.string,
  votes: PropTypes.object,
  user: PropTypes.object,
  handleSelect: PropTypes.func,
  handleDeselect: PropTypes.func
};

export default Restaurant;