[React]: Firebase with React
React
11/08/2019
Intro
For our web pages, Firebase provides us with
- Authentication
- Database
- Server
- ...
This post will mostly focus on authentication and database by creating sign in and sign up functionalities with React.
1. Set Up Firebase Project
On this page, create a project with Firebase.
      
   
    
      
   
    
Optional to import Google Analytics
Then, go to Project Overview and click </> for web application.
      
   
    
After, register app with any name and copy the selected code snippet
      
   
    
      
   
    
2. Set up JS for Firebase
First, install firebase library into your React project
$ npm i firebaseCreate a js file (filebas.js) & import required libraries for db and authentication
import firebase from "firebase/app"import "firebase/firestore"import "firebase/auth"Paste in CDN snippet & initialize
const config = {  apiKey: "AIzaSyBZskQAZgTjfvrhsXG7uLF4catPcYpyc9s",  authDomain: "somedb-db.firebaseapp.com",  databaseURL: "https://somedb-db.firebaseio.com",  projectId: "somedb-db",  storageBucket: "somedb-db.appspot.com",  messagingSenderId: "69107290236",  appId: "1:69107290236:web:9304b6b56e070e419279f9",}firebase.initializeApp(config)Export functionalities to use it elsewhere
const provide = new firebase.auth.GoogleAuthProvide()provider.setCustomParameters({ prompt: "select_account" })
export const signInWithGoogle = () => auth.signInWithPopup(provider)export const auth = firebase.auth()export const firestore = firebase.firestore()export default firebase3. Enable sign in method
On Firebase console, go to "Authentication" and click "Set up sign-in method"
      
   
    
Then, enable sign-in methods for Google and for Email/Password.
      
   
    
Note: Enabling "Email link" will require user to verify their email address.
4. Create Sign in with Google
Let's create sign in with Google function before we implement our own sign up and sign in.
Import
import { signInWithGoogle } from "/firebase/firebase.js"Create button for pop up
<button onClick={signInWithGoogle}> Sign in with Google </button>At this point, the page is aware that there is an user authentication (We still need to store auth data to our db in later step).
      
   
    
5. Add sign out functionality
In App.js
//...import { auth } from "/firebase/firebase.js"
class App extends React.Component {  constructor() {    super()    this.state = {      curUser: null,    }  }
  undoAuth = null  componentDidMount() {    this.undoAuth = auth.onAuthStateChanged(user => {      this.setState({ curUser: user })      // console.log(user);///    })  }  // Handle Auth changes  componentWillUnmount() {    this.undoAuth()  }  render() {    return (      // ...      <Header curUser={this.state.curUser} />    )  }}In Header.jsx
import React from "react"import { Link } from "react-router-dom"import { auth } from "../../firebase/firebase.utils"
const Header = ({ curUser }) => (  <div className="header">    <div className="header-items">      {curUser ? (        //   When logged in, display sign out        <div className="item" onClick={() => auth.signOut()}>          SIGN OUT        </div>      ) : (        //   When user need to log in        <Link className="item" to="/signin">          SIGN IN        </Link>      )}    </div>  </div>)export default Header6. Store user authentication into db
We still need to store user authentication into our database (Firestore).
Create Firestore(db) in Firebase
On Firebase console, go to "Cloud Firestore" or "Database". Then, create database in test mode
      
   
    
      
   
    
Side note: Getting user reference
import firebase from "firebase/app"import "firebase/firestore"
const firestore = firebase.firestore()
// Examples of retrieving collections or docsfirestore  .collection("users")  .doc("SAh79TtKh7PBjp4kFglWb8yysXl1")  .collection("someItems")  .doc("Qo0eUBTNla84Zh1BDs78")// is equivalent to..firestore.doc(  "users/SAh79TtKh7PBjp4kFglWb8yysXl1/someItems/Qo0eUBTNla84Zh1BDs78")// Getting collectionfirestore.collection("users/SAh79TtKh7PBjp4kFglWb8yysXl1/someItems/")In firebase.js, create async API request to store data into db
export const createUserData = async (userAuth, moreData) => {  // When there is no authentication  if (!userAuth) return
  // Get snap shot of user reference from the particular user in users collection  const ref = firestore.doc(`users/${userAuth.uid}`)  const snapShot = await ref.get()
  // If the snap shot info is not in db, create one  if (!snapShot.exists) {    const { displayName, email } = userAuth    const createdAt = new Date()    // store into database    try {      await ref.set({        displayName,        email,        createdAt,        ...moreData,      })    } catch (e) {      console.log("Error creating user", e.message)    }  }  // Return user reference  return ref}Call the implemented function in componentDidMount in App.js
import { auth, createUserData } from "/firebase/firebase.js"// ...class App extends React.Component {  // ...  componentDidMount() {    this.undoAuth = auth.onAuthStateChanged(user => {      createUserData(user)    })  }}Above function stores the user data in to database. Next step now is to store the data as state of our app.
7. Store data from db as state
Make the following changes in App.js to store data from db as state.
class App extends React.Component {  componentDidMount() {    this.undoAuth = auth.onAuthStateChanged(async userAuth => {      if (userAuth) {        // Use ref to retrieve data by onSnapshot method        const ref = await createUserData(userAuth)
        // Grab data in db        ref.onSnapshot(snapShot => {          this.setState(            {              curUser: {                id: snap.id,                ...snapShot.data(),              },            }            //, () => {            //   console.log(this.state); /// debug            // }          )        })      }      // On sign out, change curUser to null      else {        this.setState({ curUser: userAuth })      }    })  }}8. Create Sign up component
import React from "react"import { auth, createUserData } from "/firebase/firebase.js"
class SignUp extends React.Component {  constructor() {    super()    this.state = {      displayName: "",      email: "",      password: "",      passwordConfirm: "",    }  }  handleSubmit = async event => {    event.preventDefault()    const { displayName, email, password, passwordConfirm } = this.state    if (password !== passwordConfirm) {      alert("passwords don't match")      return    }    // authenticate & create user data    try {      const { user } = await auth.createUserWithEmailAndPassword(        email,        password      )      await createUserData(user, { displayName })      // Action after form submission (clear form)      this.setState({        displayName: "",        email: "",        password: "",        passwordConfirm: "",      })    } catch (error) {      console.error(error) ///    }  }  handleChange = event => {    const { name, value } = event.target    this.setState({ [name]: value })  }
  render() {    const { displayName, email, password, passwordConfirm } = this.state    return (      <div className="sign-up">        <h2 className="title">Sign up</h2>        <span>Sign up with your email and password</span>        <form className="sign-up-form" onSubmit={this.handleSubmit}>          <input            type="text"            name="displayName"            value={displayName}            onChange={this.handleChange}            label="Display Name"            required          />          <input            type="email"            name="email"            value={email}            onChange={this.handleChange}            label="Email"            required          />          <input            type="password"            name="password"            value={password}            onChange={this.handleChange}            label="Password"            required          />          <input            type="password"            name="passwordConfirm"            value={passwordConfirm}            onChange={this.handleChange}            label="Confirm Password"            required          />          <button type="submit">SIGN UP</button>        </form>      </div>    )  }}export default SignUp9. Create sign in component
import React from "react"import { auth, signInWithGoogle } from "../../firebase/firebase.utils"
class SignIn extends React.Component {  constructor(props) {    super(props)    this.state = {      email: "",      password: "",    }  }  handleSubmit = async event => {    event.preventDefault()    const { email, password } = this.state    try {      await auth.signInWithEmailAndPassword(email, password)      // After logging in      this.setState({ email: "", password: "" })    } catch (e) {      alert(e) ///    }  }  handleChange = event => {    const { value, name } = event.target    this.setState({ [name]: value })  }  render() {    return (      <div className="sign-in">        <h2>Login</h2>        <span>Sign in with your email and password</span>        <form onSubmit={this.handleSubmit}>          <input            name="email"            type="email"            handleChange={this.handleChange}            value={this.state.email}            label="email"            required          />          <input            name="password"            type="password"            handleChange={this.handleChange}            value={this.state.password}            label="password"            required          />          <div className="buttons">            <button type="submit"> Sign in </button>            <button onClick={signInWithGoogle}>Sign in with Google</button>          </div>        </form>      </div>    )  }}export default SignIn