Firebase で静的サイトのホスティングと ESLint・Prettier の適用までやったので、Firebase Database を使って定番の ToDo アプリを作ってみたい。
ただ実際に写したり動かしたり色々検討しながらなので、内容がかなり乱暴になっているのはご容赦いただきたい。
- 9/24 大幅に書き直し、ちゃんとしました。
前提作業
事前にアプリの雛形の作成と、ESLint・Prettier の設定を行っておく。
まあ ESLint・Prettier の設定は必須では無いけど。。。
Firebase Database の初期化
まずは Firebase Database を扱う為に、設定ファイルの生成や初期化を行う。
firebase init database |
するとdatabase.rules.json
が作成され、firebase.json
に database の設定が追記される。
ToDo アプリの作成
構成
ToDo アプリは以下の構成にする。
- index.js : App.js を呼び出すエントリーポイント
- App.js : アプリ全体のレイアウトを持つコンポーネント
- TodoList.jsx : TodoList のコンポーネント
- Todo.jsx : Todo 1 個に対するコンポーネント
- InputForm.jsx : Todo を登録するフォームのコンポーネント
index.js
index.js はエントリーポイントとなるファイルになる。
import React from 'react'; |
処理としては#root
の要素に App コンポーネントをレンダリングするのみだ。
ここはある程度お決まりの記述になる。いわゆるおまじないだ。
registerServiceWorker は create-react-app が自動生成したモジュールになる。ぶっちゃけ何をしているのか分かっていないので、近いうちに内容をよく確認したい。。。
App.js
App.js は実質的なルートになるコンポーネントになる。
ここではタイトルを表示し、TodoList コンポーネントを読み込んでいる。
import React, { Component } from 'react'; |
ちなみにhero
やcontainer
などのクラスはBulmaを利用している。以下のように CDN で利用できるので非常にカンタンだ。
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css"> |
最初はMaterial-uiなどのコンポーネントセットの利用も検討したが、パラメータなどが多く、React 初心者にはちょっと辛そうだったので今回は CSS フレームワークにした。
TodoList.jsx
次に Todo アイテムをリスト保持するコンポーネントを作成する。
まだ Firebase database と繋いでいないのでとりあえず初期値をベタ書きしているが、このコンポーネントは Todo アイテムのリストを状態(state)として保持する。
import React, { Component } from 'react'; |
Todo コンポーネントを map で作成し、ul 配下に展開している。ちなみに{...todo}
は要素を全て Todo コンポーネントの props に引き継ぐ記述だ。
Todo.js
最後に Todo 1 個あたりのコンポーネントを作る。
import React, { Component } from 'react'; |
基本的には受け取った props の title や description を表示するだけ。それなりにタグが色々あるように見えるのは Bulma の panel を使っているからだ。
ここで一度実行(yarn start
)すると以下のような画面が立ち上がる。
このようにベタ書きではあるが Todo が表示されれば OK だ。
Firebase Database 適用
ここまでで静的な Todo アプリ(アプリと言ってよいのだろうか。。。?)が出来たが、ここに Firebase database との連携を追加して動きのあるページにしていく。
まず Firebase SDK をインストールする。前に入れた Firebase CLI とは別物で、Firebase Database 等へのアクセスが可能になる。
yarn add firebase |
Database アクセス用のモジュール作成
Firebase Database へのアクセスを簡易にするためモジュールを作成します。
- firebase/config.js
- firebase/index.js
まずは config.js に Firebase へアクセスする為の設定情報を持たせます。
Firebase Concole にアクセスし、以下のボタンから接続情報を確認する。
確認できた接続情報を以下のようにfirebase/config.js
に持たせる。
export const firebaseConfig = { |
続いてfirebase/index.js
で config を読み出し、初期化したうえで export する。
今回は database のみなので、firebaseDb
のみを export する。
import firebase from 'firebase'; |
これで各コンポーネントからは以下のように呼び出すことで、DB を利用することができるようになる。
import { firebaseDb } from './firebase'; |
初期データのロード
初期データのロードということで、Firebase Database に登録されているデータを初回読み込み時にロードする。現時点ではデータを登録していないけど、データ登録処理を入れた時にスムーズにいくよう先にやっとく。
TodoList.jsx を以下のように書き換える。
import React, { Component } from 'react'; |
state の todos をただの空配列にし、componentDidMount
メソッドを追加した。このメソッドで Firebase Database のデータをロードし、state の todos に設定する。
constructor と componentDidMount で処理を分けているのはデータのロードが非同期だからで、コンポーネントマウント時に改めて setState している。
DB のデータを取得するメソッドとして、ここではon
を利用している。ざっくり言うとデータに変化がある度にロードするon
と、一回限りロードするonce
がある。主な違いは以下の通りだ。
on
on メソッドでは'value'
とコールバック関数が引数として渡される。
database.ref('***').on('value', callback); |
ここで渡された callback はリスナーに登録され、データの状態を監視するようになり、データに変化があり次第 callback をキックするようになる。
今回の用途ではsnapshot => this.setState({ todos: snapshot.val() || [] })
というコールバックを登録しているので、データに変化がある度にこの関数が実行される。
ちなみに on メソッドは戻り値としてリスナーに登録された callback 自体を返す。リスナーの登録を解除したい場合は、この返された callback を利用して off メソッドを呼んでやればいい。
// onでリスナー登録 |
once
once メソッドでも'value'
とコールバック関数が引数として渡されるが、on と異なりリスナーを登録するわけでは無く一度のみデータを取得する。
database.ref('***').once('value', callback); |
また、on ではコールバックとしてでしか関数を呼べなかったが、once は Promise を返すこともできる。
// これもOK |
id を key にする
もう一つ変化点として、todos のデータの持ち方を変えている。
データをベタ書きしていた時は、todo 1 件のデータは以下のように id を持っているが
{ |
次のようなデータの持ち方に変更する。(次項参照)
id: { |
何故かというと、id は Firebase Database が一意に作成するものを使用するようにする為、データの中に id というフィールドを持つのではなく、データのキーとして id を持つようになるからだ。(フィールドとして id を持ってもいいけど、冗長になるので排除する)
そのため、todos を map で回す際は以下のように書き換える。key と value に分離して、props の id には明示的に key を設定してあげるようにする。
{ |
データ登録フォームの作成
次に新規の ToDo タスクを登録するための入力フォームをコンポーネントで作成する。
InputForm.jsx としよう
import React, { Component } from 'react'; |
入力フォームではタスクの Title と Description を登録できるようにする。
constructor(props) { |
コンストラクタでは state の初期化と、イベントの bind を行う。イベントの bind はReact.createClass ではなく React.Component を使う場合に必要なようだ。
submit の onClick で firebase の API を呼び出す。
onClick() { |
.push()
を使ってデータ登録することにより、Todo に対して一意の ID を付与しつつ登録することができる。
そしてこのコンポーネントを App.js から呼びだす。
<section className="container"> |
以下のような画面になるはずだ
Todo を消せるようにする
最後に Todo を削除できるようにする。
完了のチェックボックスを付けて、チェックしたら削除ボタンが押せるようになる。だと Todo リストっぽい(よね?)
以下のように Todo コンポーネントに処理を追加する。
import React, { Component } from 'react'; |
チェックボックスを付ける
チェックボックスを追加する。チェック状態はthis.props.checked
と同期させ、クリックするとhandleCheck
という関数を呼ぶようにする。
<input |
呼ばれる関数は以下のものだ
handleCheck() { |
props の id をキーとして todos からデータを指定し、checked のみを update かける。ここで update を掛けると DB の変更を TodoList.jsx で検知するので、checked が変更されたデータが自動的にロードされる。
これでチェックボックスをクリックする度にチェックが切り替わる動作ができるようになる。
削除ボタンを付ける
次に、チェックボックスが ON になったら表示される削除ボタンを追加する。
{ |
チェックボックスと同様に、クリックするとhandleDelete
関数を呼ぶ。
handleDelete() { |
props の id をキーとして DB からデータを削除する。これも update と同様に、データの変更を検知して、画面表示の一覧が自動的に更新される。
さいごに
Todo リストとして必要最小限の機能は実装することはできたが、React も Firebase もギリギリ動かせたというのが正直なところ。
しかしこのお手軽さでリアルタイムな Web アプリを作れるのは非常に可能性を感じる。もっと色々いじり倒してみたいものだ。
環境
- Windows 10
- React 16.4.2
- create-react-app 1.5.2
- Firebase 5.3.1