Phoenix + Reactでチャットアプリを作ろう(Part 2)
React Hooksを使ったGroupの作成・参照画面作成
アプリ概要はPhoenix + Reactでチャットアプリを作ろう(Intro)参照。
ここまでのコードはP1_3_ADD_CORSブランチ参照。
Part 1で紹介したGroup一覧取得、Group作成、Group詳細のJSON REST APIが構築されていることを前提とする。
本章(Part 2)ではReact Hooksを使って3つの画面を作成する。
グループ一覧画面(Group List)
- グループ名一覧:グループ名をクリックするとグループ詳細画面に遷移
- 新規作成ボタン:ボタンをクリックすると新規グループ作成画面に遷移
グループ詳細画面(Group Detail)
- メッセージ一覧:表示のみ
- 新規メッセージフォーム:ユーザー名とメッセージ分を入力して、Sendボタンで送信できる
グループ新規作成画面(New Group)
・グループ名を入力して、Createボタンで送信できる
作業項目は以下の通り。
- プロジェクトファイル作成と初期設定:カスタムな内容ではないけど、サーバを起動すればブラウザからアクセスできる状態
- Home 画面作成
- スタイルガイド作成:コンポーネント間で使い回せるスタイルを定義する。
- Group詳細画面作成
- Group作成画面作成
プロジェクトファイル作成と初期設定
create-react-appを使うとReactアプリの枠組みを作ってくれる。
プロジェクトのルートで作成しよう。ディレクトリ名はapiと揃うようにwebなどが妥当だろう。ただ、アプリ名ではないので、chatterでアプリを自動生成してから、ディレクトリ名だけwebにリネームする。
cd chatter
npx create-react-app chatter
mv chatter web
新しくできたアプリをnpm run startで立ち上げると画面が見えるはず
cd web
npm run start
最初のテストを書こう。
とりあえず、ヘッダーの文字が表示されることを確認しよう。
テストを通すための最小限の変更をすると、
パスするはず。
npm test
次に、画面遷移にはルーターが必要なのでreact-routerをインストールしよう。
npm install --save react-router-dom
BrowserRouterとSwitchを使えば、各画面にパスを紐付けられる。
アプリを renderしたらデフォルトでHome画面の”My Chat Groups”の文字列が表示されるようテストを更新して、
BrowserRouterを使った実装をしよう。
テストを走らせれば、成功するはずだ。
npm test
ブラウザで画面遷移を確認してみよう。
最後に、スタイルも追加しよう。入れ子構造が使えるSCSSの方が整理しやすいので、SCSSを可能にするライブラリをインストールしよう。
npm install --save node-sass
App.cssからApp.scssにリネームしてスタイルを充ててみよう。
これでApp.scssをApp.jsにimportしてから
ブラウザを確認すると、
スタイルが反映されている。ナイス!
それでは、Home画面を本格的に作っていこう。
Home 画面作成
とりあえずsrc
ディレクトリの中にReactのコンポーネントを入れるComponentディレクトリを作って、その中にHome画面に関連するHomePage
ディレクトリを作り、HomePage.js、HomePage.scss、HomePage.test.jsの空ファイルを入れてあげよう。
cd src
mkdir -p Component/HomePage
cd Component/HomePage
touch HomePage.js HomePage.scss HomePage.test.js
cd ../../..
まずHomePage.test.jsを書いてみよう。以前書いたApp.test.jsに内容の近いテストだ。
npm test
でテストを走らせると失敗するので、成功させるための実装をする。
それをApp.jsにimportすれば、全てのテストが通るはずだ。
次にGroupsの一覧が表示されることをテストしよう。
複雑なテストになるので2つのステップに分けて考えよう。
- ① 画面表示と同時にApiリクエストを実行
- ② 取得できたGroups一覧を表示
①に関しては、React HooksのuseEffectを使えばコンポーネントのロード時にリクエストを実行することができる。Repository Patternを使って、Groups一覧のPromiseを取得できるGroupRepoクラスが存在することを前提とすればテストが書き易くなる。
②に関しては、①が成功するまで表示内容の確認ができないので、非同期処理をの同期的に書くためのasync awaitを使う。
テストを書く前に、Groupのデータクラスを定義しよう。
mkdir src/Domain
API側で定義されているGroupと一致することが重要だ。
テストファイルのテストの下に、上記①の説明で書いたGroupRepoを定義しよう。テスト用の決まったデータを扱うテストダブルなので、StubGroupRepoと呼ぶ。
StubGroupRepoとasync/awaitを使えばこの様なテストが書ける。HomePageをrenderする時に、StubGroupRepoを依存注入しているのが分かる。
テストが通るように実装をしよう。
React Hooksにまつわるロジックを上から説明すると、
const [group, setGroups] = useState([])
ReactのuseState Hookを利用してgroupという変数を定義し、デフォルト値を空配列にしている。
useEffect(() => {
props.groupRepo.getList()
.then(groups => setGroups(groups))
}, [props.groupRepo])
レンダー時に実行されるuseEffect Hookを利用。props経由で依存注入されたgroupRepoのgetList()を呼び、戻ってきたgroupsを先ほど定義した変数groupにセットする。最後に、再実行の条件として依存している変数を配列に追加すれば、変更があった場合useEffectが再実行される。
これでhomePage.test.jsのテストはパスするはずだが、App.test.jsが失敗する。理由を見てみると、
HomePage.test.jsではStubGroupRepoを注入しているが、 App.test.jsでは注入できていないので失敗する。
ようやく「本物」のGroupRepoを作成する必要ができた。
mkdir src/Repo
テストで定義したSpyGroupRepoと同じメソッドを追加しよう。
これでテストは通るはずだ。
次に、空配列ではなくAPIリクエストとJSON処理の実装を追加したい。テストから書いてみよう。
fetch
をWrapperオブジェクトで包んで、それをSpyとして注入すれば、テストも実装もスムーズにいく。
これで画面を表示すると、PhoenixのAPIで作成したGroupsが表示される!
最後に画面遷移のテストを追加しよう。
データ取得の時と同様、外部(別コンポーネント)との連携なので、別クラス(redirectService)にロジックをまかせて依存注入しよう。
あとは実装すれば必要な機能が揃うはず。
RedirectServiceを依存注入するためには、App.jsでRedirectServiceを初期化する必要がある。<BrowserRouter/>
を<Router history={createBrowserHistory()}/>
と置き換えれば、
そのhistory
を使ってhistory.push(route)
で画面遷移できる。
あとはHomePageから呼び出すだけ。
これでHome Pageができた!
ここまでのコードはP2_1_HOME_PAGE参照。