[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 firebase
Create 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 firebase
3. 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 Header
6. 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 SignUp
9. 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