Cloud Firestore คือบริการฐานข้อมูลประเภท NoSQL ที่จัดเก็บข้อมูลแบบ Online ซึ่งสามารถใช้งานได้ฟรีหากอยู่ในข้อจำกัดที่กำหนดไว้ บทความนี้เราจะลองมาเขียนโปรแกรม CRUD ด้วย React Hooks เพื่อใช้งานบริการฐานข้อมูลฟรีตัวนี้กันดูครับ
โดยบทความนี้จะไม่ขอพูดถึงพื้นฐานเกี่ยวกับ Cloud Firestore และฐานข้อมูลประเภท NoSQL นะครับ
เราสามารถใช้ Google Account เพื่อสมัคร Firebase Account และสร้าง Firebase Project ได้ที่ https://console.firebase.google.com
หลังจากสร้าง Firebase Project ก็สามารถดูค่า Configuration เพื่อใช้สำหรับการตั้งค่าในขั้นตอนต่อไป ได้จากการคลิ๊กตามลำดับที่ 1.
และ2.
Path
+-src
| |-firebase.js
| |-Insert.jsx
| |-Display.jsx
| |-Delete.jsx
| |-Update.jsx
พิมพ์คำสั่งที่ Terminal
# ติดตั้ง package firebase
npm install @firebase/app --save
npm install @firebase/firestore --save
เพื่อความสะดวกในการเรียกใช้ Instance ของ Firebase Application จากไฟล์ Component แต่ละไฟล์ เราจึงแยกเขียน โค้ดในส่วนนี้ออกเป็น Module ซะเลย
ไฟล์ src/firebase.js
// เรียกใช้ module
import firebase from '@firebase/app'
import '@firebase/firestore'
// ค่า minimum configuration คือ `apiKey` และ `projectId`
const config = {
apiKey: 'AIzaSyCSZnKyvz6a8aBIAzLSG...',
projectId: 'tutor4dev-...'
}
export default firebase.apps[0] || firebase.initializeApp(config)
Component ที่ต้องการใช้งาน Instance ของ Firebase Application เราก็จะทำการ Import Module และจะใช้ตัว Instance ดังกล่าวในการสร้างตัวแปรเพื่อเข้าถึง Collection ชื่อ users
ตามลำดับ
ในฐานข้อมูลแบบ NoSQL นั้นเราสามารถอ้างถึงชื่อ Collection ทั้งๆที่เราไม่เคยสร้าง Schema ของ Collection นี้มาก่อน
Code
// เรียกใช้ module
import firebaseApp from '../firebase'
// ประกาศตัวแปรเพื่ออ้างอิง user collection
const db = firebaseApp.firestore()
const userCollection = db.collection('users')
มาเริ่มต้นที่ Component ตัวแรกซึ่งทำหน้าที่ Insert ข้อมูล กันเลยครับ
ไฟล์ src/Insert.jsx
import React, { useState } from 'react'
/* โค้ดการเรียกใช้ firebaseApp */
function Insert() {
// ประกาศตัวแปร state และ method สำหรับเปลี่ยนค่าตัวแปร
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
async function insertDocument() {
// เว้นส่วนนี้ไว้ในขั้นตอนถัดไป
}
return <div>
{/* ตัวแปร state จะถูกเปลี่ยนค่าเมื่อพิมพ์ข้อมูล และ trigger การ re-render */}
<input type="text" value={ firstName } onChange={e => setFirstName(e.target.value)} />
<input type="text" value={ lastName } onChange={e => setLastName(e.target.value)} />
{/* เรียกใช้ method เมื่อ click ปุ่ม */}
<button onClick={ insertDocument }>Save</button>
</div>
}
ขั้นตอนต่อไปจะเป็นการเขียนโค้ดสำหรับ Insert ข้อมูลไปยัง Cloud Firestore ครับ โดยเราได้ใช้ State firstName
และ lastName
เป็นข้อมูลที่นำไป Insert
หลังจากการ Insert แล้ว Cloud Firestore จะคืน Reference Object ของ Document ที่เพิ่งจะถูก Insert สำเร็จ ซึ่งเราสามารถใช้ Object นี้เข้าถึงค่าที่เราต้องการได้ เช่น Document ID เป็นต้น
ไฟล์ src/Insert.jsx
async function insertDocument() {
// insert และคืน document reference
const documentRef = await userCollection.add({
firstName,
lastName
})
// ใช้ document reference เข้าถึงค่า document id
alert(`new document has been inserted as ${ documentRef.id }`)
}
ข้อมูลถูกจัดเก็บใน Cloud Firestore แบบ Collection และ Document
Cloud Firestore รองรับการผูก Subscription แบบ Realtime เช่นเดียวกับ Firebase Realtime Database ในขั้นตอนนี้เราต้องใช้งาน useEffect
Hooks เพื่อจัดการกับ Lifecycle Method ของ React.js
ไฟล์ src/Display.jsx
// เรียกใช้ module
import React, { useState, useEffect } from 'react'
/* โค้ดการเรียกใช้ firebaseApp */
function Display() {
// ประกาศตัวแปร state
const [ users, setUsers ] = useState({})
useEffect(() => {
// subscription นี้จะเกิด callback กับทุกการเปลี่ยนแปลงของ collection users
const unsubscribe = userCollection.onSnapshot(ss => {
// ตัวแปร local
const users = {}
ss.forEach(document => {
// manipulate ตัวแปร local
users[document.id] = document.data()
})
// เปลี่ยนค่าตัวแปร state
setUsers(users)
})
return () => {
// ยกเลิก subsciption เมื่อ component ถูกถอดจาก dom
unsubscribe()
}
}, [])
return <div>
{/* แสดงผล state users */}
<pre>{ JSON.stringify(users) }</pre>
</div>
}
users
State
{
"QJRxdiPlN4B9tuVUJd4T": {
"firstName": "Steve",
"lastName": "Jobs"
},
"saysTLfSQBFxCFh7NR7s": {
"firstName": "Dan",
"lastName": "Abramov"
}
}
Delete
Componentการจัดการกับ Document ของ Cloud Firestore นั้นเราจะต้องใช้เลข ID ของ Document ในการอ้างอิง ขั้นตอนนี้จึงจะเป็นการนำ users
State มาแสดงคู่กับปุ่ม Delete โดยปุ่ม Delete จะส่งค่า Argument ซึ่งเป็นค่า ID ของ Document นั้นๆ
ไฟล์ src/Delete.jsx
function Delete() {
/* โค้ด realtime subscription */
async function deleteDocument(id) {
// ประกาศตัวแปรเพื่ออ้างอิงไปยัง document ที่จะทำการลบ
const documentRef = userCollection.doc(id)
// ลบ document
await documentRef.delete()
alert(`document ${ id } has been deleted`)
}
return <div>
{ Object.keys(users).map(id => {
return <button key={ id } onClick={ () => deleteDocument(id) }>Delete { id }</button>
}) }
</div>
}
Update
ComponentUpdate
Component จะทำงานเป็น 2 ขั้นตอน ขั้นตอนแรกจะใช้ค่า Document ID อ่านค่า Document รายการที่ต้องการแก้ไขมาเก็บไว้กับตัวแปร State ซึ่งผูกกับ onChange
Event ของ <input />
ขั้นตอนที่ 2 จะเป็นการใช้ค่าปัจจุบันของตัวแปร State บันทึกไปยัง Cloud Firestore
ไฟล์ src/Update.jsx
function Update() {
async function readDocument(id) {
// ประกาศตัวแปรเพื่ออ้างอิงไปยัง document ที่จะทำการลบ
const documentRef = await userCollection.doc(id).get()
// อ่านค่าของ document
const { firstName, lastName } = documentRef.data()
// เปลี่ยนแปลง state ตามค่าของ document
setId(documentRef.id)
setFirstName(firstName)
setLastName(lastName)
}
async function updateDocument() {
// update ค่าไปยัง cloud firestore โดยใช้ค่าจาก state
const options = { merge: true }
await userCollection.doc(id).set({ firstName, lastName }, options)
}
return <div>
<p>ID: { id }</p>
</div>
}