<info 343/>

Firebase

Joel Ross
Autumn 2016

View of the Day

  • Firebase
    (extended example)

Setup React App

Install Dependencies

cd path/to/project
npm install

Start Development Server

npm start

always do this!

A "web backend solution"

{
  "key":{
    "property":"value",
    "property":"value"
  },
  "key":"value",
  ...
}

Setup Firebase

  1. Sign up at https://firebase.google.com/

  2. Create a New Project

  3. Add Firebase library (already installed) to your index.js

import firebase from 'firebase'; //import library

// Configure Firebase (replace with your app data)
// refresh the page if any fields are blank!
var config = {
  apiKey: "apiKey",
  authDomain: "projectId.firebaseapp.com",
  databaseURL: "https://databaseName.firebaseio.com",
  storageBucket: "bucket.appspot.com"
};
firebase.initializeApp(config);

Enable Authentication

Conditional Rendering

Use logic in your render() function to decide what HTML elements to show.

class LoginControl extends React.Component {
  render() {
    var isLoggedIn = this.props.isLoggedIn;

    var button = null;
    if (isLoggedIn) {
      button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />;
    }

    return (
      <div>
        <Greeting isLoggedIn={isLoggedIn} />
        {button}
      </div>
    );
  }
}

Creating Users

firebase.auth().createUserWithEmailAndPassword(email, password)
   .then(function(firebaseUser) {
      console.log('user created: '+firebaseUser.uid);
      //...
   })
   .catch(function(error) { //report any errors--useful!
      console.log(error);
   });

Lifecycle Methods

React components have a number of "lifecycle methods" that are automatically called by the framework when the components are added or removed from the DOM. You can override these methods to specify actions that should occur at these times.

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    // called when element is created,
    // but may not be added to the DOM yet!    
  }  

  componentDidMount() {
    // called when component has been added to the DOM
  }

  componentWillUnmount() {
    // called when component about to be removed
  }

}

Responding to Login

firebase.auth().onAuthStateChanged(firebaseUser => {
   if(firebaseUser){
      console.log('logged in');
      //assign firebaseUser.uid to `userId` using this.setState()
   }
   else {
      console.log('logged out');
      //assign null to `userId` using this.setState()
   }
});

Arrow Functions

ES 6 provides a shortcut syntax for declaring functions using =>

//normal function declaration
let foo = function(param) {
  return 'foo '+param;
}
//arrow function declaration (block body)
let foo = (params) => {
  return 'foo '+params;
}
//arrow function declaration (concise body)
let foo = (params) => 'foo '+params;

single expression implies return

//arrow function declaration (concise body)
let sayHello = (name) => console.log('Hello '+name);

anonymous function assigned to a variable

Arrow Functions

ES 6 provides a shortcut syntax for declaring functions using =>

//normal function declaration
let array = [1,2,3];
array.map(function(num) {
  return num*2; //multple each item by 2
});
//arrow function declaration (block body)
let array = [1,2,3];
array.map(num => {
  return num*2; //multple each item by 2
});
//arrow function declaration (concise body)
let array = [1,2,3];
array.map(num => num*2);

Arrow Functions

ES 6 provides a shortcut syntax for declaring functions using =>

//normal function declaration
let array = [1,2,3];
array.map(function() { //no params! (for reasons)
  return 12; //map everything to 12
});
//arrow function declaration (block body)
let array = [1,2,3];
array.map(() => {
  return 12; //map everything to 12
});
//assign function to a name
let make12 = () => 12;
//arrow function declaration (concise body)
//assign function to a name
let make12 = function() {
  return 12;
};

Syntactic Sugar causes cancer of the semicolon

Arrow Functions

Arrow functions do not create their own this context, so that the this will continue to refer to the contained scope

class MyComponent extends React.Component {
  fetchData() {
    var thisComponent = this; //1. save "this" for later
    fetch(url)
      .then(function(res) {return res.json();};
      .then(function(data) {
        thisComponent.setState({ /*...*/ }); //2. use saved "this"
      });
  }
}

remember this problem?

class MyComponent extends React.Component {
  fetchData() {

    fetch(url)
      .then(function(res) {return res.json();};
      .then(data => {
        this.setState({ /*...*/ });
      });
  }
}

this refers to "outer" (component) scope

class MyComponent extends React.Component {
  fetchData() {

    fetch(url)
      .then(res => res.json());
      .then(data => this.setState({ /*...*/ }) );
  }
}

Optional, but increasingly common and leads to cleaner code!

Realtime Database

Firebase Security Rules

Specify who can access what elements in the "JSON Object in the Cloud"

{
  "rules": {
    ".read": true,
    "users": {
      "$uid": {
        ".write": "auth.uid === $uid"
      }
    }
  }
}

Database References

Get a reference variable (think "url") for an element in the database.

//get reference to the "root" of the database: the containing JSON
var rootRef = firebase.database().ref();

//refers to the "users" value in the database
var usersRef = firebase.database().ref('users');

//refers to the "ada" value inside the "users" value in the database
var adaUserRef = firebase.database().ref('users/ada');

Concurrency and Arrays

var array = ['a', 'b', 'c'];

Given an array:

//add element 'ab' at index 1 (between 'a' and 'b')
array.splice(1, 0, 'ab');

Person 1 says:

//change element at index 1 (the 'b') to 'B'
array[1] = 'B';

Person 2 says:

var array = ['a', 'ab', 'B', 'c']; // ??

What does the array look like??

var array = ['a', 'B', 'c']; // ??

Listen for DB Changes

var usersRef = firebase.database().ref('users');



usersRef.on('value', (snapshot) => {
    var newVal = snapshot.val();




    this.setState({users:newVal});
});

reference to value in db

register listener

listen for value changes

arrow function so `this` stays in scope

convert "firebase object" into normal JavaScript value

What we did...

WE MADE TWITTER

Action Items!

 

Lab: Team formation and GitHub practice

         Show up to get on a team!!

Wed: Single-page applications

info343au16-firebase

By Joel Ross

info343au16-firebase

  • 1,641