終於來填這個坑。
解到死的 bug(React Native) 放置頂是因為當初一直找不到關鍵字來解這個 bug,所以花了一段時間才解出來。
總之呢,這個 bug 是只要把 firebase 引入來用時,就會出現編譯失敗然後顯示下面的錯誤訊息:
While trying to resolve module ‘idb’ from file … the package was successfully found. However, this package itself specifies a main module field that could not be resolved. Indeed, none of these files exist:
簡單來說這是因為 firebase 使用 .cjs
的副檔名,而 Expo 跟 React Native 不支援,所以你要手動調整一些設定來讓他們支援。
在根目錄建立 metro.config.js
,填入底下內容:
1 2 3 4 5 6 7 8 const { getDefaultConfig } = require ("@expo/metro-config" );const defaultConfig = getDefaultConfig(__dirname);defaultConfig.resolver.assetExts.push("cjs" ); module .exports = defaultConfig;
如果是 React Native cli 的話:
1 2 3 4 5 6 7 8 9 const { getDefaultConfig } = require ("metro-config" );const { resolver : defaultResolver } = getDefaultConfig.getDefaultValues();exports .resolver = { ...defaultResolver, sourceExts : [ ...defaultResolver.sourceExts, "cjs" , ], };
這樣就解決了,詳細可以參考 官方文件 或這篇 討論 。
基礎概念 Firebase 中有兩個主要的東西:
collection 可以想成是 table 的感覺
document 可以想成是欄位的意思
Firebase 是透過「路徑」來表示資料庫,比如說我有這樣的東西:
把資料轉換成網址的話就會像上面這張圖一樣。
懶人包 API general
initializeApp
初始化,initializeApp(firebaseConfig)
getFirestore
建立 DB 的 reference,getFirestore(app)
doc
建立 document 的 reference
getDoc
取得單一個 document(注意沒有 s)
getDocs
一次取得 collection 中的所有 document
setDoc
建立或更新 document
deleteDoc
刪除 document
collection
用來設定 reference 的方法,例如:collection(db, 'orders')
serverTimestamp
取得時間戳(可用來設定 createdAt)
query 相關
query
拿來下指令
orderBy
排序
limit
限制數量
前置作業 首先:expo install firebase
附註:建議裝的套件 npm install tslib
(能避免噴 Error)
先建立專案
跳轉後選擇「web」來建立 App
把 config 的內容記下來,等一下會用到
建立 database
從側邊欄點選「firestore database」
點選「建立資料庫」
點選「已測試模式啟動」
點選「啟用」
完成後就可以建立 collection(table) 了。
如果想要設定一些限制,可以到「規則」這個 tab 來設定。
跟資料庫連線(初始化) 詳細可以參考這個官方文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { initializeApp } from "firebase/app" ;import { getFirestore } from "firebase/firestore" ;const firebaseConfig = { apiKey : "..." , authDomain : "..." , projectId : "..." , storageBucket : "..." , messagingSenderId : "..." , appId : "..." }; const app = initializeApp(firebaseConfig);export const db = getFirestore(app);
基本的 CRUD Create(setDoc) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import { db } from "../firebase" ;import { doc, setDoc } from "firebase/firestore" ;export default function Com1 ( ) { const create = () => { const docRef = doc(db, 'MyCollection' , 'MyDocument' ); const docData = { name : 'PeaNu' , age : 30 , job : 'Front-End' } setDoc(docRef, docData) .then(() => alert('Create Succuess!' )) .catch(err => alert(err.message)) } return <button onClick ={create} > Create Document</button > }
Read(getDoc) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import { db } from "../firebase" ;import { doc, setDoc, getDoc } from "firebase/firestore" ;import { useEffect, useState } from "react" ;export default function Com1 ( ) { const [user, setUser] = useState(null ); const read = () => { const docRef = doc(db, 'MyCollection' , 'MyDocument' ); getDoc(docRef) .then(snapshot => { if (snapshot.exists()) { setUser(snapshot.data()); } else { alert('No doc found' ); } }) .catch(err => alert(err.message)) } return ( <> {user && ( <div > <h2 > Name: {user.name}</h2 > <p > Age: {user.age}</p > <p > Job: {user.job}</p > </div > )} <button onClick ={read} > Read Document</button > </> ) }
如果 collection 或 document 不存在的話就會幫你建立一個新的。反之,如果存在的話就會幫你把內容更新。
Update(setDoc) 一樣是用 setDoc
這個方法,不過會稍微做點變化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 import { db } from "../firebase" ;import { doc, setDoc, getDoc } from "firebase/firestore" ;import { useState } from "react" ;export default function Com1 ( ) { const [user, setUser] = useState(null ); const [value, setValue] = useState("" ); const read = () => { const docRef = doc(db, 'MyCollection' , 'MyDocument' ); getDoc(docRef) .then(snapshot => { if (snapshot.exists()) { setUser(snapshot.data()); } else { alert('No doc found' ); } }) .catch(err => alert(err.message)) } const update = (data, merge ) => { const docRef = doc(db, 'MyCollection' , 'MyDocument' ); setDoc(docRef, data, { merge : merge }) .then(() => { alert('Update Success' ) read() }) .catch(err => alert(err.message)) } return ( <> {user && ( <div > <h2 > Name: {user.name}</h2 > <p > Age: {user.age}</p > <p > Job: {user.job}</p > </div > )} <button onClick ={() => update({name: value}, true)}>Update Document</button > <input style ={{ display: 'block ', margin: '20px 0 '}} type ="text" placeholder ="Update name" onChange ={e => setValue(e.target.value)} value={value} /> </> ) }
Delete(deleteDoc) 刪除總是最單純:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { db } from "../firebase" ;import { doc, deleteDoc } from "firebase/firestore" ;export default function Com1 ( ) { const Delete = () => { const docRef = doc(db, 'MyCollection' , 'MyDocument' ); deleteDoc(docRef) .then(() => { alert('Delete Success' ); }) .catch(err => alert(err.message)) } return <button onClick ={Delete} > Delete Document</button > ) }
複雜一點的操作(下 Query) 假設我的需求是這樣:
抓出 collection 中的所有 document
根據 document 資料裡的時間戳排序,取出最新的一筆資料
這就會用到 query
,一個可以讓我們下指令的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { db } from "../firebase" ;import { getDocs, collection, query, orderBy, limit } from "firebase/firestore" ;import { useEffect } from "react" ;export default function Com1 ( ) { useEffect(() => { const q = query(collection(db, 'orders' ), orderBy('createdAt' , 'desc' ), limit(1 )); getDocs(q).then(querySnapshot => { querySnapshot.forEach(document => { console .log('Document' , document .data()); }) }) }, []) return (...) }
這邊要特別注意,如果你試著用 console.log(querySnapshot)
是拿不到真正的資料的,因為在 firebase 中「取值」這件事都得透過 getter
,所以才會用 forEach
搭配 .data()
來取出 document 的值。
參考資料