Firebase 入門(4) ~ Todo アプリをデプロイできるのか? (2)

この数日、Firebase Authentication と格闘してた。一応目処が立って Firebase realtime database を使ってのTodoアプリも完成したんだけど、FIrebase Authentication は 「Todo アプリをデプロイできるのか?」というとこの本質ではなかったなぁと思った。ただ、ちゃんとしたアプリを作りたい場合は必須の知識なので無駄ではなかったと信じたい。

typescript との連携

typescript だからと言って特に何もすることはなかった。

realtime database reference

database.ref(アドレス) で Reference オブジェクトが取得できる。このReference オブジェクトを使って値の取得だったり、値の追加だったりをする。

on() メソッド

on() メソッドは値をサブスクライブするのに使用する。逐一値をポーリングすることが不要になるので便利。

value

第一引数に"value" を指定すると、アドレスで指定した場所以下すべてのデータが返ってくる。一部を変更したとしても全部が返ってくる。

database.ref(`todos/${uid}`).on("value", function(snapshot) => {
  // todos/$uid 以下のデータがすべて送られてくる
  // {
  //    "a": {
  //      "id": "a",
  //      "text": "todo 1",
  //      "completed": false
  //    },
  //    "b": {
  //      "id": "b",
  //      "text": "todo 2",
  //      "completed": false
  //    }
  // }
  // 値は snapshot!.val() で取れる
  console.log(snapshot!.val())
})

child_added

child_added は項目の追加があった場合に追加部分だけが送られてくるので、データ量を少なくしたい場合に便利。

ref.on("child_added", function(snapshot) => {
  // todos/$uid/変更のあったデータ 以下のデータがすべて送られてくる
  // "value" の時とフォーマットが少し異なるので注意が必要
  // {
  //   "id": "a",
  //   "text": "todo 1",
  //   "completed": false
  // }
  // 値は snapshot!.val() で取れる
})

child_removed

child_removed は項目の削除があった場合に削除された項目部分だけが送られてくる コードは child_added と変わらないので省略

child_changed

child_changed は項目に更新があった場合に更新があった箇所を含むブロックが送られてくる。例えば、以下のような構造があったとして

{
  "a": {
    "id": "a",
    "text": "todo 1",
    "completed": false
  },
  "b": {
    "id": "b",
    "text": "todo 2",
    "completed": false
  }
}

"completed" を true にするように変更したとしたら、"b" 以下のデータブロックが送られてくる。child_added, child_removed 以外のなにがしかの交信がここで捕捉され通知されるといった感じだろうか。

ref.on("child_changed", function(snapshot) {
  // true になって送られてくる
  // { "id": "b", "text": "todo 2", "completed": true }
  console.log(snapshot!.val())
})

completed のトグル

先ほどの例のように "completed" を true にする。みたいな場合は以下のようにonceと組み合わせてsetを行う。

const database = firebase.database();

const ref = database.ref(`todos/${uid}/${id}/completed`)
ref.once('value').then((snapshot) => {
  ref.set(!snapshot.val())
})

Action と Action Dispatcher

firebase realtime database が on() メソッドによってデータベースの交信をリアルタイムに通知してくれる関係で、それをトリガにアクションを作成し、dispatcher に投げられるようになった。ただ、Viewからのアクションをaction_dispatcherで捕捉しないとdatabaseへアクセスできなくなるので、View → Action Dispatcher の流れと Firebase → Dispatcher の2種類の流れができてしまう。これはもう仕方ないのかな?と割り切っているが、どうにかできるんだろうか。

View → Action Dispatcher の流れは今までと変わらないので省略。

Firebase → Dispatcher の流れは基本的に Firebase の on() メソッドで捕捉したデータをもとにアクションを作成して dispatcher に渡しているだけだ。Reducer が type によって action と state をマージするという流れは変わらない。

  // Firebase Realtime Database 側
  todosref.on('child_added', function(snapshot) {
    if(snapshot!.val() !== null) {
      dispatch({
        type: 'todos/child_added',
        payload: snapshot!.val()
      })
    }
  }, function(error:any) {
    console.log(error)
  })
  // reducer 側
  case 'todos/child_added':
    if(action.payload === undefined || action.payload === null) {
      return state
    }
    return [
      ...state,
      action.payload
    ]

まとめ

一応、Firebase realtime database と連携させることで Todo アプリをデプロイすることができたが、色々コード的に汚い部分があるので、今後、思いついたタイミングで編集していこうと思う。

成果物

Merge pull request #19 from bamchoh/firebase · bamchoh/react-study@69cbf7b · GitHub