How to use Real-time DB of Firebase
- How to do initial setting
- Structure of Database
- How to make CRUD function
- Efficient State management
- How to make table
How to do initial setting(how to find database)
Creates and initializes a Firebase app instance.
firebase.js
import firebase from "firebase/app";
import "firebase/database";
import dotenv from "dotenv";
dotenv.config();
var firebaseConfig = {
apiKey: process.env.REACT_APP_APIKEY,
authDomain: process.env.REACT_APP_AUTHDOMAIN,
databaseURL: process.env.REACT_APP_DATABASEURL,
projectId: process.env.REACT_APP_PROJECTID,
storageBucket: process.env.REACT_APP_STORAGEBUCKET,
messagingSenderId: process.env.REACT_APP_MSGSENDERID,
appId: process.env.REACT_APP_APPID,
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
let realTimeDB = firebase.database().ref(); //how to find real-time db location
export default realTimeDB;
firebase.database.Reference
: A Reference represents a specific location in your Database and can be used for reading or writing data to that Database location.
Comparison with the case of Cloud Firestore / Storage
import firebase from "firebase/app";
import "firebase/storage"; //store images
import "firebase/firestore"; //Cloud firestore
import "firebase/auth";
import dotenv from "dotenv";
dotenv.config();
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
var firebaseConfig = {
apiKey: process.env.REACT_APP_APIKEY,
authDomain: process.env.REACT_APP_AUTHDOMAIN,
projectId: process.env.REACT_APP_PROJECTID,
storageBucket: process.env.REACT_APP_STORAGEBUCKET,
messagingSenderId: process.env.REACT_APP_MESSAGESENDER,
appId: process.env.REACT_APP_APPID,
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const projectStorage = firebase.storage(); //This reference points to the root of Cloud Storage bucket.
const projectFirestore = firebase.firestore();
const timeStamp = firebase.firestore.FieldValue.serverTimestamp;
const projectAuth = firebase.auth();
export { projectStorage, projectFirestore, timeStamp, projectAuth };
Structure of Database
1) Realtime Database
Stores data as one large JSON tree.
- Simple data is very easy to store.
- Complex, hierarchical data is harder to organize at scale.
2) Cloud Firestore
Stores data as collections of documents.
- Simple data is easy to store in documents, which are very similar to JSON.
- Complex, hierarchical data is easier to organize at scale, using subcollections within documents.
- Requires less denormalization and data flattening.
Common Sense
- Both Realtime Database and Cloud Firestore are NoSQL Databases.
- Client-first SDKs, with no servers to deploy and maintain
- Realtime updates
- Free tier, then pay for what you use
Other Differences
Realtime DB |
Cloud Firestore |
|
Offline support | iOS and Android clients |
iOS, Android, and web clients |
Presence(User) | supported |
Not supported natively |
Querying | Deep queries with limited sorting and filtering functionality |
Indexed queries with compound sorting and filtering |
Writes and transactions | Basic write and transaction operations |
Advanced write and transaction operations |
Reliability and performance | a regional solution |
a regional and multi-region solution that scales automatically |
Scalability | Scaling requires *sharding |
Scaling is automatic |
Security | Cascading rules language that separates authorization and validation |
Non-cascading rules that combine authorization and validation |
*Sharding: a database architecture pattern related to horizontal partitioning — the practice of separating one table’s rows into multiple different tables, known as partitions.
For more detail: nesoy.github.io/articles/2018-05/Database-Shard
source from: firebase.google.com/docs/firestore/rtdb-vs-firestore?hl=en
How to make CRUD function
C.R.U.D.
1. Create / Update
"addOrEdit" method
1) Get input info from ContactForm.js when submit button clicked
2-1) If Edit button is not clicked, push new info to database
2-2) If Edit button is clicked, set new info to the element accordingly by using currenId
2. Read
"useEffect" method
Snapshot database
3. Delete
"handleDelete" method
Delete selected data by using currenId
Contacts.js
function Contacts() {
const [contactObjs, setContactObjs] = useState({})
const [currentId, setCurrentId] = useState("")
const addOrEdit = obj =>{
if(currentId === ""){
realTimeDB.child('contacts').push(
obj,
err => {
if(err) {
console.log(err)
} else {
setCurrentId('')
}
})
} else {
realTimeDB.child(`contacts/${currentId}`).set(
obj,
err => {
if(err) {
console.log(err)
} else {
setCurrentId('')
}
})
}
}
const handleDelete = (currentId) => {
if(window.confirm('Are you sure to delete this record?')){
realTimeDB.child(`contacts/${currentId}`).remove(
err => {
if(err) {
console.log(err)
} else {
setCurrentId('')
}
})
}
}
useEffect(() => {
realTimeDB.child('contacts').on('value', snapshot => {
if(snapshot.val() != null) {
setContactObjs({
...snapshot.val()
})
} else {
setContactObjs({})
}
})
},[])
return (
...
ContactForm {...({ addOrEdit, currentId, contactObjs})}/>
...
<tbody>
{
Object.keys(contactObjs).map(id=>{
//๊ฐ์ฒด ๊ทธ๋ฃน์ผ ๋ Object.keys ๋ฅผ ์ด์ฉํ์ฌ ๋ฐฐ์ดํ ์ํฌ ์ ์๋๋ฐ,
//map ์ ๊ฐ ์์๋ id(๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์์๋ก ๋ง๋ id๊ฐ) ์ด๋ค.
return (
<tr key={id}>
<td>{contactObjs[id].fullName}</td>
<td>{contactObjs[id].mobile}</td>
<td>{contactObjs[id].email}</td>
<td>
<div className="btn text-primary" onClick={() => setCurrentId(id)}>
<i className="fas fa-pencil-alt"></i>
</div>
<div className="btn text-danger" onClick={() => handleDelete(id)}>
<i className="fas fa-trash-alt"></i>
</div>
</td>
</tr>)
})
}
</tbody>
)
}
export default Contacts;
ContactForm.js
import React, {useState, useEffect} from "react";
function ContactForm( { addOrEdit, currentId, contactObjs} ) {
const initialFieldValues = {
fullName:"",
mobile:"",
email:"",
address:""
}
const[values, setValues] = useState(initialFieldValues);
const handleInputChange = e =>{
const { name, value } = e.target;
setValues({...values, [name]: value})
}
const handleFormSubmit = (e) => {
e.preventDefault();
addOrEdit(values);
}
//edit ์ ํด๋ฆญํ๋ฉด id ๊ฐ์ด ์ ๋ฌ๋๋ค.
useEffect(()=>{
if(currentId === ""){
setValues({...initialFieldValues})
} else {
setValues({...contactObjs[currentId]})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
},[currentId, contactObjs])
return (
<form action="" autoComplete="off" onSubmit={handleFormSubmit}>
<div>
<div className="form-group input-group">
...
<input className="form-control" placeholder="Full Name" name="fullName" value={values.fullName} onChange={handleInputChange}/>
</div>
<div className="form-group input-group">
...
<input className="form-control" placeholder="Mobile Number" name="mobile" value={values.mobile} onChange={handleInputChange}/>
</div>
<div className="form-group input-group">
...
<input className="form-control" placeholder="E-mail" name="email" value={values.email} onChange={handleInputChange}/>
</div>
<div className="form-group input-group">
...
<input className="form-control" placeholder="Address" name="address" value={values.address} onChange={handleInputChange}/>
</div>
</div>
<div className="form-group submit">
<input type="submit" value={currentId === "" ? "Save":"Update"} className="btn btn-primary btn-block"/>
</div>
</form>
)
}
export default ContactForm;
Efficient State management
Utilize name / value props to update values(fullName, mobile, email, address) easily.
import React, {useState, useEffect} from "react";
function ContactForm( { addOrEdit, currentId, contactObjs} ) {
const initialFieldValues = {
fullName:"",
mobile:"",
email:"",
address:""
}
const[values, setValues] = useState(initialFieldValues);
//here is the point!
const handleInputChange = e =>{
const { name, value } = e.target;
setValues({...values, [name]: value})
}
...
return (
<form action="" autoComplete="off" onSubmit={handleFormSubmit}>
<div>
<div className="form-group input-group">
...
<input className="form-control" placeholder="Full Name" name="fullName" value={values.fullName} onChange={handleInputChange}/>
</div>
...
</form>
)
}
export default ContactForm;
How to make table
Concept
<table>
<thead> : defines a set of rows defining the head of the columns of the table.
<tr> : Table Row Element (<tr>) defines a row of cells in a table
<th>menu</th> : defines a cell as header of a group of table cells
<th>price</th>
</tr>
</thead>
<tbody> : encapsulates a set of table rows that comprises the body of the table
<tr>
<td>steak</td>
<td>45000</td>
</tr>
<tr>
<td>wine</td>
<td>45000</td>
</tr>
</tbody>
<tfoot> : Its value will be null if there is no such element.
<tr>
<td>Total</td>
<td>103000</td>
</tr>
</tfoot>
</table>
<table className="table table-borderless table-stripped">
<thead className="thead-light">
<tr>
<th>Full Name</th>
<th>Mobile</th>
<th>Email</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{
Object.keys(contactObjs).map(id=>{
return (
<tr key={id}>
<td>{contactObjs[id].fullName}</td>
<td>{contactObjs[id].mobile}</td>
<td>{contactObjs[id].email}</td>
<td>
<div className="btn text-primary" onClick={() => setCurrentId(id)}>
<i className="fas fa-pencil-alt"></i>
</div>
<div className="btn text-danger" onClick={() => handleDelete(id)}>
<i className="fas fa-trash-alt"></i>
</div>
</td>
</tr>)
})
}
</tbody>
</table>