少し前にテスト環境やテストコードの書き方の書き方に付いてのブログを書きましたが、最近あるアプリのUIテストを書いていました。
一般論として、テストがあるとリファクタリングを行った直ぐ後にコードの動作を確認でき、テストを書いておく事でリファクタリングしやすくなります。
しかし今回気が付いた事は、コード(コンポーネント)がほぼ完成した時点でテストを書いたので、改めてコード全体を眺めコンポーネントやHooksの統一性の低さでした。
Stable Diffusionが生成した画像です
なぜ統一性が低かったのか
たとえばバックエンドを作る場合は、データーベース設計を行ったり、ドメインモデルを考慮しながら作って行くので、ある程度の統一性があるコードになると思います。しかし今回はReactのUIコンポーネントなので、画面デザインや仕様を見ながらページ単位で実装して行きました。
UIコンポーネントは見た目がデザイン通りで仕様通りの動きをすれば良いので、どのように作ったら良いのかなどは深く考えずにノリで作って行けます。😅
今回のアプリ作成では最初にルーティングやAPI通信周り、広域のステート管理などを考えていまいしたが、UIコンポーネントのReactコードは気楽に書き進めていきました。
コンポーネントを作る中で、他のページでも使いそうなコンポーネントを共通コンポーネントとして切り出したり、他のページでも使うロジック等をカスタムHooksとして切り出していきました。
今回リファクタリングした項目
共通コンポーネントの統一性を高めた
ほぼ完成した共通コンポーネントを眺めてみると統一性が低い事に気が付きました。たとえば共通コンポーネント内のリンク(ボタン)に対して
- クリックしたさいの遷移処理がコンポーネントに書き込まれているもの
- 遷移先パスをProps(引数)で渡すもの
- クリック時の処理(Callback)をPropsで渡すもの
などがありました😅。 今回は2.に統一しました。
React RouterのuseOutletContextを止めた
このアプリでは、React Router V6のレイアウト機能を使っています。レイアウト・コンポーネントとレイアウトの上にのる子コンポーネントは独立していますが、レイアウト上にあるタイトルを子コンポーネントから設定したくなります。
そのような場合は、useOutletContextを使うことで、レイアウトコンポーネントの持つステート等を子コンポーネントからアクセスできるようになります。
ただし、レイアウトコンポーネントと子コンポーネントが密結合になるのでテストが書きにくくなります。またコードも理解しずらくなります。
そこでuseOutletContextを止め、Recoilを使った広域ステート管理に変更しました。
カスタムhooksのインターフェイスを統一した
このアプリでもカスタムHooksを使ってロジック等を共有しています。しかし1つHooksが複数のAPI(関数)を持つ場合の、呼び出し(import)方法が
- 主要機能が
defaut export
されていて、補助的な機能は関数としてexport
されている - Hookの戻り値が、機能毎の複数の関数のオブジェクトになっている
のパターンがありましたが、2.に統一しました。
まとめ
私はテストコードは後付けで書くほうなのですが、テストコードを書く時点で対象のコードを一括で、かつ冷静な目で見直しできるのが、とても良い事だと感じました。