# Firebase Realtime DatabaseをReactで使ってみる〜読み込み編〜

- 公開日: 2020-06-26
- タグ: Firebase, React
- URL: https://tminasen.dev/blog/20200626

> Firebase Realtime DatabaseをReactから使う方法の読み込み編。Firebase CLIの設定、DBルールの変更、SDKの初期化からデータ取得とコンポーネントでの表示までを備忘録としてまとめている。

# はじめに

Firebase Realtime DatabaseをReact使ってみた今日このごろ。  
備忘録も兼ねて使い方をまとめておく。

まとめていたら思ったより長くなったので、今回は読み込みまでやる。

今回はReact(クライアント側)の設定だけで、Firebaseのプロジェクトの設定などは割愛する。  
※一応やったことはWebUIポチポチしただけ。

# TL;DR.

[コード](https://github.com/tminasen/program-samples/tree/1b0b9dc2edd5fdca291d5f70fd3491b1b77fddb8/realtime-db-react)

# Reactプロジェクト作成

`create-react-app`からスタートする。  
サクッと用意。

```bash
$ npx create-react-app realtime-db-react --template typescript
```

# Firebase周りの設定

## プロジェクトの準備

FirebaseCLIを使って設定を進める。  
設定は[公式のリファレンス](https://firebase.google.com/docs/cli) を見ながら行う。  
また、プロジェクトは作ってある前提とする。

```bash
# ログインしていなければログインする
$ npx firebase login

# Firebaseプロジェクト初期化
$ npx firebase init
# 設定した内容は下記の通り
>? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. 
❯◉ Database: Deploy Firebase Realtime Database Rules

? Please select an option: 
❯ Use an existing project

? Select a default Firebase project for this directory: 
❯ 作成しておいたプロジェクト

# デフォルトのまま
? What file should be used for Database Rules? database.rules.json

✔  Firebase initialization complete!
```

## DBのルール変更

上記手順で作ったままではDBに読み書きできないようになっているので少し修正する。  
生成されている`database.rules.json`を下記のように修正。

```json
{
  "rules": {
    // 読むのは誰でも可能
    ".read": true,
    // 書くのは認証済みの場合のみ
    ".write": "auth != null"
  }
}
```

## ルール反映

json変えたではもちろん反映されないので、`firebase deploy`コマンドを叩いて反映する。  
反映できているかどうかは下記手順で確認できる。  
表示されるURLからコンソールを表示  
→左カラムにある`Database`を選択  
→メインカラム上部のプルダウンから`Realtime Database`を選択  
→ルールタブを表示

```bash
$ npx firebase deploy
# ↓が表示されればOK
✔  Deploy complete!
Project Console: https://console.firebase.google.com/project/hogehoge/overview
```

## テスト用データ追加

確認用にWebUIからデータ追加しておく。  
追加したのは↓の感じ。

```json
{
  "sample": {
    "key1": "value1",
    "key2": "value2"
  }
}
```

# 実装

## Firebase SDKとFirebase Admin SDKのインストール

アプリからFirebaseを使うために必要なライブラリをインストールする。  
adminの方は認証がいる場合に必要なのでどちらも入れておく。

```bash
$ yarn add firebase firebase-admin
```

## 初期化周り

初期化に必要な情報をWebUIから取ってくる。  
プロジェクトのページ左カラムにある歯車をクリック  
→プロジェクトの設定をクリック  
→Settingsページ、全般タブ下側にあるマイアプリにある`</>`マークをクリック  
→ウェブアプリにFirebaseを追加のページに飛ぶので、お好みの名前を入れて登録をクリック(hostingはお好みで。今回はやらない)  
→スクリプトが表示されるので、`firebaseConfig`の内容だけコピーする

必要な情報を取れたのでコードを書いていく。  
初期化はこれでOK

```tsx
import firebase from 'firebase/app';
// 認証周りやDB周りで必要なためimportしておく
import 'firebase/auth';
import 'firebase/database';

// コピーしてきたfirebaseConfigそのまま
// 元がvarで宣言されているので、constに変更
const firebaseConfig = {
  // コピペ
};

firebase.initializeApp(firebaseConfig);
export {firebase};
```

## データベースに接続する

データのやり取りは`firebase.database.Reference`を通してやり取りされる。  
今回は決まったパスのデータを取得したいので、パスを指定した上でメモ化しておく。

```tsx
// カスタムフックにしておく
const useDatabase = () => {
  // 同じパスでは毎回同じ結果が得られるのでmemo化しておく
  return useMemo(() => firebase.database().ref('/sample'), []);
};
```

## データを取得する

↑で作ったReferenceを受け取る関数を作成する。  
Referenceのイベントをlistenすることでデータを取得できる。  
指定したパスのデータに対する更新をすべて検知するには`value`を指定すれば良い。

```tsx
// hooksを使いたいのでカスタムhooksにしておく
const useFetchData = (ref: firebase.database.Reference) => {
  const [data, setData] = useState<{ [key: string]: string }>();
  useEffect(() => {
    // イベントリスナーを追加するにはonを使う
    ref.on('value', snapshot => {
      // パスに対する全データを含むsnapshotが渡される
      // ない場合はnullが変えるので存在をチェックしておく
      if (snapshot?.val()) {
        setData(snapshot.val());
      }
    });
    return () => {
      ref.off();
    };
    // refの変更に応じて再取得する
  }, [ref]);
  // データを返却する
  return {data};
}

// 実際に呼び出す際はこちらを使う
export const useFetchAllData = () => {
  // refを取得して
  const ref = useDatabase();
  // ref渡してデータを取得する
  return useFetchData(ref);
};
```

## Componentからデータ取得する

`useFetchAllData`を使ってデータを取ってくるコンポーネントを作成する。  
取得したデータはobject形式なので、list形式に変換してから表示する。

```tsx
import React, {useMemo} from 'react';
import {useFetchAllData} from '../firebase/firebaseDB';

export const ListComponent: React.FC = () => {
  // dataを取ってくる
  const {data} = useFetchAllData();
  // object形式なので使いやすいように{key, value}形式のリストに変換する
  // また、データが変わらない限り結果は同じなのでメモ化しておく
  const dataList = useMemo(() => Object.entries(data || {}).map(([key, value]) => ({key, value})), [data]);

  return <dl>{dataList.map(({key, value}) =>
    <React.Fragment key={`${key}${value}`}>
      <dt>key: {key}</dt>
      <dt>value: {value}</dt>
    </React.Fragment>
  )}</dl>
};
```

起動した際に画像のようにデータを表示できればOK。
![ss20200626](/images/article/20200626/ss20200626.png)

# まとめ

今回はReactでFirebase Realtime Databaseを使ってデータの読み込みができるところまでやった。  
最初にも書いたけど思ったより長くなったので今回はここまでにしておく。

# 参考サイト

- [Firebase CLI リファレンス](https://firebase.google.com/docs/cli)
- [Firebase を JavaScript プロジェクトに追加する](https://firebase.google.com/docs/web/setup?hl=ja)
- [サーバーに Firebase Admin SDK を追加する](https://firebase.google.com/docs/admin/setup?hl=ja)
- [ウェブでのデータの取得 | Firebase](https://firebase.google.com/docs/database/web/retrieve-data?hl=ja)
