Day 7: Firebase

Wednesday, May 24, 2017

Lecture Videos

Morning:

Afternoon:

Topics

React

JavaScript

  • Property initializers + arrow functions

Firebase

  • Getting started
  • Database rules
  • Re-base for syncing React state with Firebase

Examples

React

Methods as props

Sometimes one component needs to update another component’s state. It can’t do that directly, but it can call a method from that other component if it’s available via a prop.

Try this example live on CodePen


  
import React from 'react'
import ReactDOM from 'react-dom'

const PartyButton = ({ celebrate, celebrations }) => {
  return <button onClick={celebrate}>Party! {celebrations}</button>
}

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      celebrations: 0,
    }
    this.celebrate = this.celebrate.bind(this)
  }

  celebrate() {
    const celebrations = this.state.celebrations + 1
    this.setState({ celebrations })
  }

  render() {
    return <PartyButton celebrate={this.celebrate} celebrations={this.state.celebrations} />
  }
}

ReactDOM.render(<App />, document.querySelector('main'))

  
  

Component lifecycle methods

componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here.



import React, { Component } from 'react'

class MyComponent extends Component {
  componentDidMount() {
    this.nameInput.focus()
  }

  render() {
    return (
      <input 
        ref={(input) => { this.nameInput = input; }} 
        defaultValue="will focus"
      />
    )
  }
}


react-contenteditable package

This package provides a React component for a div with editable contents, handling all the messy stuff (like dangerouslySetInnerHTML).



import React from 'react'
import ContentEditable from 'react-contenteditable'

class MyComponent extends React.Component {
  constructor() {
    this.state = {
      html: "<strong>Hello, <em>World</em>!</strong>"
    }
    this.handleChange = this.handleChange.bind(this)
  }

  handleChange(ev) {
    this.setState({ html: evt.target.value })
  }

  render() {
    return (
      <ContentEditable
        html={this.state.html}       // innerHTML of the editable div
        disabled={false}             // use true to disable
        onChange={this.handleChange} // handle innerHTML change
      />
    )
  }
}


JavaScript (ES6+)

Property initializers

Yesterday, we used property initializers to set a component’s initial state without adding a constructor. Combining property initializers and arrow functions also gives us a convenient way to auto-bind this:



class Something extends React.Component {
  handleButtonClick = (ev) => {
    // `this` is bound correctly!
    this.setState({ buttonWasClicked: true });
  }
}


Firebase

Getting Started

Firebase is a real-time database hosted by Google. In addition to the database, it also provides features of authentication, analytics, cloud storage, and hosting. For Thing List, we synced the state of our app to our database on Firebase. This allowed all of our data to be persisted, even after page refreshes.

Re-base is an open source package that allows easy syncing of local state with a Firebase database. Add rebase to your project with one of the following commands:

user@localhost ~
 
yarn add re-base               # add package using yarn
npm install --save re-base     # add package using npm

Once you have re-base installed, setup is easy! First, create a new project on Firebase, then click on “Add to a web app” to see your JavaScript config object. Next, initialize a Firebase app and database in your project using the config object, and provide the database to re-base.



import Rebase from 're-base'
import firebase from 'firebase/app'
import database from 'firebase/database'

const app = firebase.initializeApp({
  apiKey: "YOURAPIKEY",
  authDomain: "YOURAUTHDOMAIN",
  databaseURL: "YOURDATABASEURL",
  projectId: "YOURPROJECTID",
  storageBucket: "YOURSTORAGEBUCKET",
  messagingSenderId: "YOURSENDERID"
})

const db = database(app)
const base = Rebase.createClass(db)

export default base


Finally, call base.syncState to sync your app’s local state with Firebase. The first argument to syncState is the name of the Firebase endpoint you want to sync, and the second is a configuration object.



base.syncState('myFavoriteEndpoint', {
  context: this,
  state: 'items'
})


Now, any time we update the state of our app, the changes will sync with Firebase in real time.

More Re-base Options

Re-base can do much more than just syncing state. There are methods for fetch, push, post, etc. To find out more about what all you can do with re-base, check out the README

Rules

For your Firebase database, you can set up rules (written in JSON) that specify the conditions under which data is allowed to be read or written. By default, a newly generated project will require that a user be authenticated to read or write any data.



{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}


If you do not have authentication set up yet, these values can be set to true. This allows anyone to read or write any data in the database. This can be convenient, but probably not a good idea long-term (and you will get a warning if you do that).

Additional rules can be applied per endpoint:



{
  "rules": {
    "emails": {
      ".read": true,
      ".write": "auth != null"
    },
    "texts": {
      ".read": true,
      ".write": "auth != null"
    },
    "users": {
      "$userId": {
        ".read": "auth != null && auth.uid == $userId",
        ".write": "auth != null && auth.uid == $userId"
      }
    }
  }
}


The above rules translate to:

  • texts and emails can be read by anyone, but only written by authenticated users
  • users data can be read and written only by an authenticated user whose uid matches the $userId of that item

Projects

Homework

  • When the checkbox is checked, mark the corresponding Thing as completed.
  • Be sure this gets synced to Firebase and persists across page refreshes.

Super Mega Bonus Credit

  • Add a due date to each thing.
  • Make sure it persists

Hint: HTML 5 includes an input type date, i.e. <input type="date" />