見出し画像

React公式ドキュメントで悩み解消!効果的なコンポーネント設計手法とは?


今回のテーマ:フロントエンド(画面)


こんにちは、TOPPANデジタルで AWS Top Engineers を目指している奈良です。今回のテーマは、アプリケーションの「フロントエンド(画面)」です。AWSに関する取り組みに加え、Webアプリケーションの開発も行っており、そこから得たノウハウを共有いたします!

対象読者
この記事は、フロントエンド(画面)に関わるエンジニアの方を対象としています。特に、以下のようなお悩みを抱えている方に参考になれば幸いです。

  1. 画面を構成する要素(コンポーネント)の設計方法が分からない

  2. 画面を作成したが、保守性が悪く管理が複雑になっている

はじめに:画面を構成する要素(コンポーネント)


アプリケーションの画面には、UI(画面レイアウトや構成)やUX(画面の挙動や遷移)に関するさまざまなパターンがあります。

コンポーネントとは?
コンポーネントとは、Reactライブラリにおける再利用可能なUI要素であり、画面の構成要素を定義したものです。アプリケーションの各構成要素をカプセル化し、各コンポーネントが独自の役割に基づいたロジックやスタイルを持つことができます。

文章よりも絵の方が分かり易いと思いますので、以下をご覧ください。5つのコンポーネントで構成されています。

https://ja.react.dev/learn/thinking-in-react
  1. FilterableProductTable(灰色)はアプリ全体のコンテナ。

  2. SearchBar(青)はユーザ入力を受け取る。

  3. ProductTable(紫)はユーザ入力に従ってリストを表示およびフィルタリングする。

  4. ProductCategoryRow(緑)はカテゴリごとの見出しを表示する。

  5. ProductRow(黄)は個々の製品に対応する行を表示する。

身近な例を挙げますと、画面上の「ボタン」、「入力フォーム」、「ヘッダー」、「フッター」や、それらを組み合わせた「商品リスト」、「ダッシュボード」、、etcです。

課題:コンポーネントの扱い(分割粒度、再利用性と役割)


画面を開発するうえで、前述のコンポーネントの扱いについて、以下の2つの課題があります。

1.コンポーネントの分割粒度

コンポーネントは以下のようにさまざまな粒度で構成できますが、粒度が統一されないと、品質の低下や保守性の悪化に繋がります。

  • 画面内の要素をひとつのコンポーネントにまとめた構成

  • 画面内の要素を細かく分け、複数のコンポーネントにした構成

そのため、最初に「コンポーネントの分割粒度」の方針を決めることが重要です。

2.再利用性と役割

コンポーネントは独立して作成し、他の場所で再利用することができます。同じコンポーネントを複数の場所で使用することで、コードの重複を減らし、開発効率を高めることが可能です。しかし、すべての画面に共通して使用できるコンポーネントを作るのは難しいです。画面ごとに構成や役割(機能的役割やレイアウトとしての役割)が異なるためです。
そのため、「再利用性と役割」に関する方針もあらかじめ決めておく必要があります。

それではここからは、2点の課題を解決するための方向性をご紹介したいと思います!

解決の方向性:公式ドキュメントの読み解き


まずは、Reactの開発元であるMeta(旧称Facebook)が提唱しているReact公式ドキュメントを見直します。公式ドキュメントには、「Reactの流儀」として、Reactを用いたアプリケーション開発におけるアプローチや思考プロセスが記載されています。

https://ja.react.dev/learn/thinking-in-react

公式ドキュメントでは、「役割を重視したコンポーネント化」や「co-location型(関連するコードやファイルを同じ場所にまとめるという設計手法)のディレクトリ構成」を意識した開発が推奨されており、これを基に2つの視点で掘り下げていきます。

解決の方向性:「コンポーネントの分割粒度」


仮想の商品ページをサンプルに、「コンポーネントの分割粒度」について、Step1からStep3の3段階で整理しながら解説します。

Step1:「Reactの流儀」に基づく構成

まず、Reactの推奨する「単一責任の原則」を意識し、以下の3種類のコンポーネントに分けて商品ページを構成します:

  • 緑:商品ページ専用コンポーネント:商品ページに特化し、他のページでは使用されない要素。

  • 橙:関連コンポーネント:商品ページ内の個別機能やUI要素に関連するコンポーネント。

  • 青:再利用可能な共通コンポーネント:他のページでも使用される、一般的なUI要素や機能。

このように、異なる役割を持つコンポーネントを組み合わせることで、一つのページを効率的かつ明確に構築できます。

Step2:Co-locationを意識したディレクトリ構成

次に、Step1で設計したコンポーネントを「Co-location」で整理します。これにより、各コンポーネントの役割と管理がさらに明確になります。以下のように分類することで、保守性が向上し、開発効率が高まります。

Step3:Next.js App Routerに対応したディレクトリ構成

Step3ではディレクトリ構成を検討します。Next.jsのApp Routerは厳格な規約に基づいて構築されており、Co-locationのアプローチと非常に相性が良いため、以下のようにディレクトリを構成します。

app/
┗_components/		# 汎用的なコンポーネント(atom / modules)
┗_form/				# フォーム関連のユーティリティ
┗puroduct/			# product ページ群
 ┗page.tsx			# product ページ自体
 ┗layout.tsx		# product 配下の共通レイアウト
 ┗product-list.tsx	# ProductList コンポーネント
  • 汎用的なコンポーネントはアンダースコア付きで配置し、ルーティングの対象外にする:_components/ ディレクトリに配置して、アプリ全体で利用できる共通コンポーネントとして管理します。

  • 「product」ディレクトリの作成:app/product/ 配下に商品ページ関連のコンポーネントを配置することで、App Routerでルーティングを分けやすくします。また、共通レイアウトやコンポーネントを明確に分離し、商品ページ専用の要素と全体で共有する要素の管理が容易になります。

以上のStep1〜Step3を踏まえると、コンポーネントはより小さな粒度で分けることが重要であり、これにより保守性や再利用性が大幅に向上します。また、Next.jsの構成を活用し、Co-locationと単一責任の原則を意識した設計を採用することで、効率的で整理されたアプリケーション構築が可能となります。

解決の方向性:「再利用性と役割」


次に、課題「再利用性と役割」についてです。この課題を解決するためには、以下の観点でコンポーネントを分類し、役割を明確にすることが重要です。

  • 使用範囲の分類:特定の画面でのみ使用する「専用コンポーネント」と、複数の画面で使いまわせる「共通コンポーネント」に分ける。

  • ロジックとUIの分離:データの取得や処理を行う「ロジックコンポーネント」と、利用者に情報を提供する「表示用コンポーネント」に分ける。

これを実現するための手法として、Container / Presentational パターンがあります。

Container / Presentational パターンとは?

Container / Presentational パターンは、ロジックとUIを分けて実装することで関心の分離を図るフロントエンドのデザインパターンです。この手法はDan Abramov氏によって提唱されました。

  • Container Component:ロジックの責務を持ち、データの取得や処理を行います。

  • Presentational Component:UIの責務を持ち、Propsで受け取ったデータをどのように表示するかのみを役割とします。

特にPresentational ComponentはUIにのみ関心を持ち、Propsで受け取ったデータを表示することが主な役割です。そのため、Presentational Component内部で状態を持つことは原則なく、Props以外の方法でデータを受け取ることもありません。

Container / Presentational パターンのメリットは以下の通りです。

  • コンポーネントの責務が明確になる
    ロジックは Container に、UI は Presentational にと、それぞれの責務がはっきりと分離されます。

  • 保守性が向上する
    UIの調整が必要な場合は Presentational コンポーネントのみを修正し、ロジックを変更する場合は Container コンポーネントのみを修正することで、変更内容に応じた適切な箇所の修正が容易になります。これにより影響範囲も限定されるため、保守性が高まります。

  • テストがしやすくなる
    ロジックのテストであれば Container コンポーネント、UI のテストであれば Presentational コンポーネントに対して行えばよいため、テストの範囲と内容が明確になります。

  • 再利用性が向上する
    Presentational コンポーネントは Props のみに依存しているため、定義された Props を渡せばどのコンポーネントからでも再利用が可能です。

所感:公式ドキュメントは一つの手法であることを忘れずに!


以下のポイントに基づき、課題として挙げていた「コンポーネントの分割粒度」と「再利用性と役割」に対して、一つの解決方法を提示することができました。

  • 「Reactの流儀」に基づいた単一責任の原則を意識する

  • Co-location 型のディレクトリ構成を採用し、責務を明確化する

  • Container / Presentational パターンを適用する

  • Container を小さく細分化する

  • Presentational に必要な Container を追加していく

今回の方法はあくまで一例であり、企画部門やデザイナーなど画面に関わる他の関係者ともよく協議し、状況に応じた最適な方法を見極めて進めることが重要です。

〆:最後に


最後までお読みいただき、ありがとうございました。今回はフロントエンド(画面)の一つの開発手法でしたが、今後の記事では実際の開発内容などご紹介したいと思います。
それでは、次回の記事もお楽しみに!

参考


本記事の内容は、以下のLT会でも紹介し、登壇資料も公開しています。また、「Cloud Know-How」という連載も行っておりますので、ぜひ他の記事もご覧ください。