Next.jsとCloudFirestoreの連携
設定ページのカテゴリの追加機能にて、
Cloud Firestoreのcategoriesコレクションと連携されている仕組みを理解したい。
コレクション:categories/ドキュメントID/フィールド:name,time,userId
nameはタスク名(カテゴリ)、
timeはタイマーの持続時間、
userIdは一時的に(12345678)としている。(認証機能を適応させた後に変更する想定)
Firebaseの利用に必要な手順
1.Firebaseを利用するための初期化
2.サービスを利用するための具体的な処理
FirebaseのAPIを取り出す
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/storage';
最終的にFirebaseから抽出するデータ格納先と容量を用意する
let db;
let storage;
設定情報を用意し、セキュリティ対策のため詳細をenvlocalファイルで設定する
const config = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};
設定できたのでFirebaseのinitializeAppメソッドを呼び出す(引数に設定情報config
を渡す)。
FirebaseのinitializeAppメソッドで生成されたアプリはfirebase.appsに格納されていくため、
firebase.appsにデータがゼロの場合のみ初期化する
if (!firebase.apps.length) {
firebase.initializeApp(config);
}
firebaseからデータとストレージを持ってくる
db = firebase.firestore();
storage = firebase.app().storage('gs://mitan-timer-dev.appspot.com');
Node.jsのお作法module.exports = {値};
で
任意ファイルに存在する変数や関数を、別ファイルで実行可能になる。
以下のように書くと別ファイルでimport文を書いてdb, storageの値を適切に扱えば、
アプリケーションとFirebaseとのDB連携が可能になる。
module.exports = { db, storage };
pc/lib/db.js
からFirebase(Cloud Firestore)にアクセスするための情報を持つ値db
を持ってくる
import { db } from '../../lib/db';
この配下にクラスを独自で定義して、内部のオブジェクト(主にメソッド)を書いていく。
Firebase
という独自クラスを用意して、
最初のconstructorメソッドではCloud Firestoreのコレクション(データの大元)を初期化している
class Firebase {
constructor() {
this.users = db.collection('users');
this.categories = db.collection('categories');
this.sounds = db.collection('sounds');
}
setting.tsx
にて、
扱うpc/lib/db.js
の関数
getUserId()
getCategories()
updateCategory()
createCategory()
deleteCategory()
getUserId
メソッドは非同期処理で、
この関数を呼び出すと、文字列'12345678'が返される
getUserId = async () => {
return '12345678'; //ログイン認証するまでの仮
};
setting.tsxでの処理内容は以下の通り。
1.変数userId
にgetUserId
メソッドで呼び出した値を代入
2.setUserId
メソッドでステートに格納して値を更新
また、非同期でgetCategories
メソッドを呼び出して引数にuserIdを渡している。
useEffect(() => {
async function fetchData() {
const userId = await firebase.getUserId(); //テストId
setUserId(userId);
await getCategories(userId);
}
fetchData();
}, []);
getCategories
メソッド
アロー関数の引数にはuserId
を渡して、処理内容は以下の通り。
1.データを格納するdata
という配列を用意
2.categories内のuserIdの値と、getUserIdメソッドで取得したuserIdの値が同値である場合に、
ref
にuserIdの値を格納
3.querySnapshot
メソッドを呼び出して配列dataに対して{ ...doc.data(), id: doc.id }
を格納
4.最後に新しく生成されたdataを返却
getCategories = async (userId) => {
let data = [];
const ref = this.categories.where('userId', '==', userId);
try {
await ref.get().then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
data.push({ ...doc.data(), id: doc.id });
});
});
return data;
} catch (e) {
throw e;
}
};
setting.tsxでは、
アロー関数の引数にuserIdを渡して、処理内容は以下の通り。
1.変数categories
にgetCategoriesで取得したdataを代入
2.setCategories
メソッドでcategoriesの値を更新
3.setSelectedOption
メソッドでselectedOptionの値(name, time, id .userId)を更新
※返却されたdataはquerySnapshotで生成された配列型なのでcategories[0]
4.setTime
メソッドでtimeの値を更新
※categories[0].timeで取得したカテゴリーのtimeを抽出できる
const getCategories = async (userId: any) => {
try {
const categories = await firebase.getCategories(userId);
setCategories(categories);
setSelectedOption(categories[0]);
setTime(categories[0].time);
} catch (e) {
console.error(e);
}
};
updateCategory()
メソッド
第1引数のidに一致するドキュメントに対して、第2引数のdataを上書きする
updateCategory = async (id, data) => {
try {
await this.categories.doc(id).update(data);
} catch (e) {
throw e;
}
};
createCategory()
メソッド
Cloud Firestore/categoriesにランダムIdを生成し、その配下に引数のdataをセット
createCategory = async (data) => {
try {
await this.categories.doc().set(data);
} catch (e) {
throw e;
}
};
setting.tsxでは、
handleModalChange
メソッドのアロー関数の引数にname, timeを渡して、処理内容は以下の通り。
0.条件分岐の設定値が型どおりであるなら??意味不明
1.変数categoryData
に対して、更新されたプロパティと値を設定
2.updateCategory()メソッドにselectedOption.id
とcategoryData
を渡して、データを上書き
3.getCategories()メソッドで新たなdataを返却
4.それ以外なら??意味不明
5.変数categoryData
に対して、更新されたプロパティと値を設定
6.createCategory()メソッドで新規ドキュメントIdを加えcategoryData`を渡しデータを上書き
7.getCategories()メソッドで新たなdataを返却
const handleModalChange = async (name, time) => {
if (userId) {
if (selectedOption) {
//カテゴリーの編集
const categoryData = { name: name, time: time, userId: userId };
await firebase.updateCategory(selectedOption.id, categoryData);
await getCategories(userId);
} else {
//カテゴリーの追加
const categoryData = { name: name, time: time, userId: userId };
await firebase.createCategory(categoryData);
await getCategories(userId);
}
}
setOpen(false);
};
deleteCategory()
メソッド
引数のidに対応するCloud Firestore/categories/ドキュメント(配下のフィールドの値)を削除
deleteCategory = async (id) => {
try {
await this.categories.doc(id).delete();
} catch (e) {
throw e;
}
};
setting.tsxでは、処理内容は以下の通り。
1.deleteCategory()メソッドを呼んで指定idのカテゴリを削除
2.getCategories()メソッドで新たなdataを返却
const handleListItemDelete = async (id: string) => {
try {
await firebase.deleteCategory(id);
await getCategories(userId);
} catch (e) {
console.error(e);
}
};