[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.

3 1

3 2

Optional to import Google Analytics

Then, go to Project Overview and click </> for web application.

3 3

After, register app with any name and copy the selected code snippet

3 4

3 5

2. Set up JS for Firebase

First, install firebase library into your React project

TEXT
$ npm i firebase

Create a js file (filebas.js) & import required libraries for db and authentication

JS
import firebase from "firebase/app"
import "firebase/firestore"
import "firebase/auth"

Paste in CDN snippet & initialize

JS
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

JS
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"

3 6

Then, enable sign-in methods for Google and for Email/Password.

3 7

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

JSX
import { signInWithGoogle } from "/firebase/firebase.js"

Create button for pop up

JSX
<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).

3 8

5. Add sign out functionality

In App.js

JSX
//...
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

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

3 9

3 10

Side note: Getting user reference

JS
import firebase from "firebase/app"
import "firebase/firestore"
const firestore = firebase.firestore()
// Examples of retrieving collections or docs
firestore
.collection("users")
.doc("SAh79TtKh7PBjp4kFglWb8yysXl1")
.collection("someItems")
.doc("Qo0eUBTNla84Zh1BDs78")
// is equivalent to..
firestore.doc(
"users/SAh79TtKh7PBjp4kFglWb8yysXl1/someItems/Qo0eUBTNla84Zh1BDs78"
)
// Getting collection
firestore.collection("users/SAh79TtKh7PBjp4kFglWb8yysXl1/someItems/")

In firebase.js, create async API request to store data into db

JS
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

JSX
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.

JSX
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

JSX
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

JSX
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

WRITTEN BY

Keeping a record