React/TypeScriptでFirestoreのCRUD処理を実装する

前回の記事(以下にリンク添付)ではFirebaseのAuthenticationを使って認証アプリを作成しましたが、本記事ではログインすると表示されるページにメモを追加/表示/更新/削除できる機能を実装します。データの保存にはFirebaseのCloud Firestoreを利用します。

関連記事

本記事ではReact(TypeScript)アプリにFirebaseを使った認証機能を組み込む方法を紹介します。認証はメールアドレスとパスワードを使った認証とGoogleアカウントを使った認証の2種類で行えるようにしています。見栄えは多少よ[…]

完成イメージ1

 

例によってソースコードはGitHubで公開していますので、よかったらご利用ください。

GitHub

React/TypeScriptによるFirestoreのCRUDアプリ. Contribute to ganeo/re…

完成イメージ

トップページは前回の記事で作ったものを流用しています。本記事の修正箇所はホームページになります。このページにCRUD処理を追加します。

ホームページはメモ(テキストエリア)を追加できる機能を左側に配置し、右側にはFirestoreに保存されているログインユーザーが作成したメモを作成日時が新しいもの順に表示します。さらに各メモを更新または削除できるようにしています。

インターネット(Link)にも完成版を公開しています。

トップページ(パス: /)完成イメージ1

ログインすると表示されるホームページ(パス: /home)home-screen

Firebase(Cloud Firestore)側の設定

Firebaseプロジェクトの作成とAuthenticationの設定は前回の記事(Link)をご参照ください。本記事ではCloud Firesotreのデータベースを追加します。

データベースの作成

Cloud Firestore用の画面に遷移するとデータベースを作成するためのボタンがありますのでそれをクリックしてデータベースを作成します。本記事ではテストモードを選択しロケーションはデフォルトのまま作成しました。

Firestoreの作成1

Firestoreの作成2

Cloud Firestoreのデータモデル

Cloud Firestoreのデータモデルは下図のようにコレクションという入れ物の中にドキュメントが格納されるイメージになります。RDBで言えばコレクションはテーブル、ドキュメントはレコードをイメージするとわかりやすいと思います。

Firestore_データモデル

(出典: Cloud Firestore データモデル) https://firebase.google.com/docs/firestore/data-model?hl=ja

React/TypeScriptの実装

React/TypeScriptアプリの雛形を作成

TypeScript用Reactアプリをcreate react appコマンドで作成します。

yarn create react-app react-firestore-crud-sample –template typescript
※青字イタリック体はアプリ名です。

利用するパッケージのインストール

FirebaseのSDKとルーティング用のパッケージをインストールします。上記で作成したReactアプリのルートディレクトリで次のコマンドを実行します。

yarn add firebase react-router-dom @types/react-router-dom

ソースコードの編集

認証機能は以前の記事で実装したのでそのソースコードをコピーしてから今回記事分のコードを追加していきます。

以下ではCRUD処理に関連しポイントになる箇所をピックアップして説明します。今回は前回作った認証機能用のサンプルコードでログインすると表示できるホームページにCRUD処理を集約しました。ホームページのコンポーネント名は下図のとおりです。

components

 

本アプリのデータモデルはconsts.tsとmodels.tsというファイルを作成してそこに定義しました。前者はコレクションの名前を定義しています。後者はMemoという名前でドキュメントの型を定義しました。

// consts.tx
export const collectionName = {
    memos: 'memos'
}
// models.ts
import firebase from 'firebase/app'

export type Memo = {
    id?: string  //ドキュメント識別用ID(自動採番)
    body: string  //メモ本文
    creater: string | null  //作成者のメールアドレス
    createdAt: firebase.firestore.FieldValue | null  //作成日時
    updatedAt: firebase.firestore.FieldValue | null  //更新日時
}

 

前回の記事同様にfirebaseのAPI呼び出しは一部を除いてfirebase.tsxに集約しています。以下のコードは上からデータ追加(addDoc)、データ更新(updateDoc)、データ削除(deleteDoc)用の関数です。データ読み出しだけはMemoList.tsxに記述しています。データ読み出し用のAPIはonSnapshotというデータ購読用のリスナー(データ変更を検知してくれる)を使っていてコンポーネントとの結びつきが強いため、コンポーネント内に記述しました。

// firebase.tsx
(・・・省略・・・)
export const addDoc = async (collectionName: string, memo: Memo) => {
  try {
    await db.collection(collectionName).add(memo)
  } catch (error) {
    alert(error)
  }
}

export const updateDoc = async (collectionName: string, memo: Memo) => {
  try {
    await db.collection(collectionName).doc(memo.id).set(memo)
  } catch (error) {
    alert(error)
  }
}

export const deleteDoc = async (collectionName: string, memo: Memo) => {
  try {
    await db.collection(collectionName).doc(memo.id).delete()
  } catch (error) {
    alert(error)
  }
}
(・・・省略・・・)

 

前述したMemoList.tsxではuseEffectフック内にデータ購読用のコードを書いています。データはログインユーザーのメールアドレスで絞り込んで取得しています。取得したデータは作成日時(createdAt)で並び替えてstateにセットしています。「return () => unsub()」ではアンマウント時に購読を解除するようにしています。

// MemoList.tsx
(・・・省略・・・)
useEffect(() => {
    if (user) {
      const unsub = db
        .collection(collectionName.memos)
        .where('creater', '==', user.email)
        .onSnapshot((snapshot) => {
          // DBからメモ一覧を取得
          const data = snapshot.docs.map((doc) => ({
            id: doc.id,
            body: doc.data().body,
            creater: doc.data().creater,
            createdAt: doc.data().createdAt,
            updatedAt: doc.data().updatedAt,
          }))
          // 取得したメモ一覧をcreatedAtの降順に並び替え
          data.sort((a, b) => {
            const x = a.createdAt
            const y = b.createdAt
            if (!x || !y) return 1
            if (x < y) {
              return 1
            } else { 
              return -1
            }
          })
          setMemos(data)       
        })
      return () => unsub()
    }
  }, [])
(・・・省略・・・)

後続記事

関連記事

本記事では以前FirebaseのAuthenticationとFirestoreを使って作ったWebアプリをFirebase Hostingにサクッとデプロイした後に独自ドメインを追加する方法までを解説します。 FirebaseのHo[…]

domain_settings_1
sponsor