はじめに
Reactは初心者です。チュートリアルしか触ったことありません。
基本はサーバサイドの仕事してます。
Reactの設計を考える必要が出てきたものの、ネット上は情報が散乱しててよくわからないので、個人的にまとめてみたものになります。
「これから取り組むにあたって、整理したメモ」です。実践してみた結果ではありません。(要は机上の空論なので間違ってるものもあるかもしれません)
ぜひ、ご経験者のかたいれば、ご意見いただけると助かります。
「べき」とか書いてる部分もありますが、「あくまで自分に向けての表現」であって、「誰にとってもやるべき」という意味合いでは無いです。
当たり前ですが、PJ前提が異なれば、適用する考え方も変わってくると思いますので。
絶対に読むべきもの
- 公式:https://ja.reactjs.org/docs/thinking-in-react.html
- 何はともあれ公式を見るべき
- 書籍:https://oukayuka.booth.pm/
- プロジェクトメンバーでReactに初めて関わる人は全員読んでもらうべきだと思うくらい良書。
- 最新のテクニックが整理されている上、過去の経緯も含まれて丁寧に説明されているので、これを読めばWEBの情報で変な情報を掴まずにすみそう。
- 当然この記事に書いてないような情報が本当に盛り沢山!
- 全部で3部(+1部)あります。全部読もう!
記事の前提
React: v17
想定PJ規模: そこそこ大規模
Reactのいろは
コンポーネント設計のいろは
原理原則・考え方(およびその例)
- 単一責任の原則・関心の分離
- コンポーネント指向:1コンポーネントは単一の責任を持つ(複数の責任を持たない)。
- コンポーネントは小さく保つ。
- 親の状態・子の状態に依存しないようにする。(やむを得ない場合はstateという手段がある。くらいのスタンスが良さそう。)
- 再利用性
- 可能な限りprops/stateは少ない方が基本的には再利用性は高い。
- 可変にすることで再利用性が高まる内容にとどめてprops/stateを用いる。
適用方法としての基本スタンス
- 最初のコンポーネント設計
- 大前提として、最初から完璧な設計を目指すのは避ける。実装の中で微調整が確実に生まれるため、最初はドラフト版くらいの感覚で作る。
- 設計の最初の立脚地点としてAtomicDesignの適用から考えていくのはわかりやすい。
- ただし、そのまま適用は苦しむことが多いらしい。下記の「重要な考え方」に反してまで形に拘るのは避けるのが良さそう。
- 一番最初はみんなでワーワー話しながらベースラインを決めるのが良さそう。
- Atomic Designから学ぶべき「重要な考え方」
- コンポーネントの分け方
- コンポーネント分割の基準が一定であり、わかりやすいこと。
- プロジェクト内で共通認識が持てれば良い。AtomicDesignはあくまで一例、こだわる必要はない。
- デザイナーと協業する案件の場合、デザイナーとデベロッパーで共通認識を持てる基準であること。
- 何かしらのメタファーを用いて、わかりやすい名前を見つけられるとテンションが上がる。
- AtomicDesignだと科学だが、音楽(ハーモニー、メロディ、リズム)を採用しているプロジェクトもある。https://logmi.jp/tech/articles/300657
- 再利用を想定した分割/設計であること。
- 再利用しないものを過度に分割する必要はない。
- 可読性を維持するため、階層を深くしすぎない。
- AtomicDesignは5階層。できればそれより少ない階層の方がわかりやすいはず。
- ロジックをどの層に実装するかを事前に決めておく
- PresentationComponent/ContainerComponentパターン:https://www.yuuniworks.com/blog/2018-05-18-presentational-component%E3%81%A8container-component/
- まずは静的に表示できる範囲のものを作成する=PresentationComponent
- 動作については別で定義する=ContainerComponent
- もし親子関係を定義するならPresentationComponentが親だと思う(けど、Presentationが子って記事も見かけた)
- 宣言的ということを考えるなら、Presentationが親の方が理解しやすい気がするけど・・・
- PresentationComponent/ContainerComponentパターン:https://www.yuuniworks.com/blog/2018-05-18-presentational-component%E3%81%A8container-component/
- コンポーネント分割の基準が一定であり、わかりやすいこと。
- コンポーネントの分け方
- コンポーネント設計の育て方
- 「Reactの流儀」をベースとして最初の実装および設計の調整を行う
- モックから始めよう
- Step 1: UI をコンポーネントの階層構造に落とし込む
- Step 2: Reactで静的なバージョンを作成する
- Step 3: UI 状態を表現する必要かつ十分な state を決定する
- Step 4: state をどこに配置するべきなのかを明確にする
- Step 5: 逆方向のデータフローを追加する
- 「3度目の法則」によるリファクタリングを行う
- 3度同じことを書くことになったタイミングでリファクタリングを行う。
- 再利用性を無駄に事前検討しなくて良いのが利点。
- 必要になった時に、再利用可能な形でリファクタリングする。
- コンポーネント設計を見直すタイミングを作っておく
- 最初に考えたものはドラフト版。
- どのタイミングが適切かはプロジェクトによるのでなんともいえない。
- 「Reactの流儀」をベースとして最初の実装および設計の調整を行う
ディレクトリ構造について
- クックパッドのノウハウ:https://note.com/tabelog_frontend/n/n07b4077f5cf3
- 抽象度や依存の有無に応じてディレクトリをわける
- 特定画面のみで使うのか、複数画面をまたいで使うのか?
- Entryポイントを独立させるのは保守性高そう。
- ドメインが入るか入らないかを意識してディレクトリをわける
- 単なるUIパーツなのか、動作や意味合いが含まれるコンポーネントなのか?
- 抽象度や依存の有無に応じてディレクトリをわける
- bulletproofのノウハウ:https://github.com/alan2207/bulletproof-react/blob/master/docs/project-structure.md
- 読んでみてとしか言いようがない。世界的なノウハウが溜まってる物なので、これをベースに考えるのが良さそうな気がする。
実装における基本スタンス
- 宣言的な実装にこだわりたい(Reactの流儀Step3〜5):https://qiita.com/erukiti/items/fb7bcbd9d79696579d06
- Reactに限った考え方ではなく、宣言的な実装はメインストリームになってきているため、考え方はシフトしていくべき。
- 宣言的とは、ソースコードの処理の流れを追わなくても、結果の状態が推測できるようになっていること。
- 基本的にはReact構文に従えば書き方自体は自動的にそうなるはず。
- ただし、変数名から関連性がわからず、結果の状態が推測しにくくなることはありえる。
- また、if文の乱用でもわかりづらくなりやすい。
- レビュー観点としてこの観点を持っておいてもいいかもしれない。
- 例えば、分岐の多い表示処理は結果状態を想定しづらいので、コンポーネントを分割するか、Compound Componentを用いる。
- stateは必要十分なだけ使う。(Reactの流儀Step3)
- まずは使わずに済む方法を考える。
- 次に、propsで代用できないかを考える。
- 「props drilling」など、シンプルなpropsでの実装では問題が生じる場合は、hooksのuseContext/useReducerの適用を考える。(これが実質的なstateの適用にあたる?)
- stateが必要になる場合、適用範囲を最小限に絞る。
- 単一責任の原則および性能の観点:コンポーネント分割を前提に、stateで影響するコンポーネントを減らしてもいいかも。
- 単一責任の原則および性能の観点:useMemoを乱用は避けたいが、多少の利用なら許容する。
- state管理が複雑になってきた場合はReduxの導入を検討する。
- VoidFunctionComponentを使おう。(Reactの流儀Step5を実現しやすくする?)
- propsがchildrenにアクセスできないようになっている。(より厳密なコンポーネント指向が実現しやすい)(v18でFunctionComponentから廃止予定のため、早めに対応できるようにするための存在)
- 型指定を強制できるため静的解析でエラー検知できるようになり、一定の開発に秩序をもたらしやすい。
- Presentational Component / Container Component(Reactの流儀Step1〜2と3〜5を分けて考える)
- 「表示」と「動作」を分けて記載する方が望ましいという考え方。
- 「コンポーネント分割」での分離をまずは考える。(stateなどが絡みそうなら「React Hooks」の導入かな・・・?)
- 表示:「どのように見えるか」に関心を持つ。データや振る舞いを変更しない。(JSX(.jsx/.tsx)で構成すれば良さそう?)
- 動作:「どのように動作するか」に関心を持つ。データや振る舞いを変更する。(Typescript(.ts/.tsx)で構成すれば良さそう?)
古いテクニック:ググってこれらが出てきてもすぐに飛びつかないこと
- クラスコンポーネント → 今は「関数コンポーネント」が強く推奨されている。(徐々に推奨の強さが上がってるように感じる)
- ライフサイクル → v17から大きく変わっているため、古い情報かどうか確実に確認すること。
- mixin/HOC/render props/Recompose → 今は「React Hooks」が強く推奨されている。
- PresentationalComponent/ContainerComponent → 今は「React Hooks」が強く推奨されている。が、基本的な考え方(表示と動作の分離)はまだ活用可能。
- Redux → 「React Hooks」との使い分けないしは共存のさせ方が議論されている。hooksに触れてないReduxの情報は避けた方がいいかも。
- ContextAPI → 「React Hooks」との使い分けないしは共存のさせ方が議論されている。hooksに触れてないReduxの情報は避けた方がいいかも。
- 全般的に → 「React Hooks」登場以前と登場以降で結構考え方が変わってそうなので、hooks絡めるとどうなるんだろう?みたいな考え方は持っておいた方が良いかも。
掘り下げた方が良さそうなこと
- v18機能 https://qiita.com/uhyo/items/bbc22022fe846fd2b763
- 平行レンダリング機能
- サスペンスを使ったストリーミングサーバレンダリング
注意事項
- 全てを厳密に適用しようとすると、相反する要求が発生する場面がある想定。その場合は、PJの状況に応じて判断する。
テストのいろは
- 全世界のノウハウ。といっても簡潔にまとまってるので、ひとまず見てみよう。
- React Testing Libraryは公式でサポートされるくらい有名なライブラリ。
- これを使わない手はない。
- (使い方はちょっと読み取りきれなかったけど、使うのは確定で良さそう。)
- 基本はレイアウトに関するテストのように見える。
どちらかというと途中から導入するケースの話がメイン。ただ、単体テストの位置付けは参考にできそう。
- フロントエンドでTDDはほぼ不可能。(実装前に正しい振る舞いを定義することが困難というか、実装しながら設計/調整するようなのが基本。)
- 単体テストは、今後の修正が入った時に防衛的に機能するためのものとして作るスタンスが妥当。
- UXのテストは単体テストでは評価できない。ただ、UXに影響する変数は関数側に定義されているべき。
- 単体テストを途中から導入する場合、不要な依存が切れて、モジュールの境界面が自明になった状態にたどり着くことが最初のゴール。
React Testing Libraryを使ってテストできるところはテストすればいいが、限界はありそうな予感。
- この記事以外に事例を見つけられなかった(検索能力の問題か・・・)のと、いいねが少ないのが気になる。
- 何かテストの実装で苦しむ場面あれば思い出して見返してもいいかもしれない。
おまけ
事例
- 「このリポジトリの目標は、Reactアプリケーションを開発する際のリソースとグッドプラクティスのコレクションとして機能することです。」
- という海外のリポジトリの紹介をしてくれているページ。かなりわかりやすいので読むのが良さそう。
- その上で、その先のリポジトリも一読しておいた方が良い。
- SlackのようなチャットツールをSPA(シングルページアプリケーション)で作った場合の事例。(ヘイシャでした)
- (今回自分のPJとはちょっとアプリの性質が異なるが、ある程度参考になる部分はありそう)
- 「おてつたび」さんの事例。
- 「wantedly」さんの事例
- デザインのための仕組みづくりみたいなニュアンス。
Atomic Designについて
- 公式:https://atomicdesign.bradfrost.com/
- 一番わかりやすかった日本語記事:https://uxdaystokyo.com/articles/glossary/atomic-design/
- Atomic Design自体はデザインの考え方であって、実装の世界の話ではない。実装の世界に当て嵌めようとすると苦しみが多いという話が多かった。
- でも、頑張って適用してるPJも、卒業してるPJも言ってることは一緒だった。
- 「開発者とデザイナーが同じ基準で会話するための土台の考え方を合わせる」
- 「メンテナンス性を高めるために、一定の基準・考え方でコンポーネントを分割し、適切なフォルダ構成を用意する。」:何が適切かはプロジェクトによって異なる→Reactの流儀を適用しながら見つけていくしかなさそう
- でも、頑張って適用してるPJも、卒業してるPJも言ってることは一緒だった。
- いくつかの事例
- sansanさんの例(Atomic Design導入タイプ):https://buildersbox.corp-sansan.com/entry/2022/01/06/110000
- 食べログさんの例(Atomic Design卒業タイプ):https://note.com/tabelog_frontend/n/n07b4077f5cf3
- OHEYAGOさんの例(Atomic Design卒業タイプ):https://tech.ga-tech.co.jp/entry/react-give-up-atomic-design
- Atomic Component:https://qiita.com/kahirokunn/items/b599d2cf04d2580c412c
- Atomic Componentは、Atomic Designの進化系と表現されているケースもあったけど、見た限りは、適用事例の一つのように感じた。
- 実践:https://b3s.be-s.co.jp/programming-language/javascript/2989/
- 個人的な感想
- 小さくてシンプルな案件ならそのまま適用でもいいかも?
- ある程度の規模以上は、たたき台として用いて、すぐに卒業するくらいの使い方が良さそう。
細かいテクニック
- 実装テクニック
- setStateメソッドは、2種類の指定の仕方(便宜上、絶対値設定と、相対値設定とでも呼ぶか?)を使い分けよう
- 三項演算子よりもnullish coalescingがスマートな場合がある
-
{}
unknown
null
は使い分けたい(他にもありそう - SyntheticEvent:元々のイベントを抑制して、自身のイベントを優先して動かす。
- 実装テクニック集
- cssテクニック集
- 性能テクニック集
さいごに
いやー、情報量が多い。
ひとまずこれをベースに始めてみようかしら。
実践した結果は余裕あれば記事にします。
ご意見・ご指摘あれば是非是非!!!
以上です。