React・実務の道具立て

コードの置き場所と分け方

どこに何を置くか、コンポーネントをいつ分けるか、レビューで指摘されるのはどこか。正解が1つではない領域なので、判断の基準と、機能ごとにまとめる構成への育て方を扱います。

ここまで作ってきたことりずかんは、こういう形になっています。

src/
├── components/     BirdCard, AddBirdForm
├── pages/          BirdListPage, BirdDetailPage, NotFoundPage
├── hooks/          useBirds, useFavorites
├── types.ts
├── App.tsx
└── main.tsx

これは小〜中規模の定番の形です。ただし正解は1つではないので、この回では判断の基準を扱います。

種類ごと → 機能ごと

最初は種類ごとcomponents / hooks / pages)で足ります。ファイルが少ないうちは、これがいちばん分かりやすいからです。

破綻するのは、機能が増えたときです。

components/
├── BirdCard.tsx
├── BirdFilter.tsx
├── AddBirdForm.tsx
├── UserAvatar.tsx
├── UserSettingsForm.tsx
├── NotificationBell.tsx
└── …(30ファイル)

「鳥の機能を直したい」ときに、30個の中から関係するものを探すことになります。そこで機能ごとにまとめ直します。

src/
├── features/
│   ├── birds/
│   │   ├── BirdCard.tsx
│   │   ├── AddBirdForm.tsx
│   │   ├── useBirds.ts
│   │   └── types.ts
│   └── user/
│       ├── UserAvatar.tsx
│       └── useUser.ts
├── shared/          ← 機能をまたいで使う部品(Button, Modal など)
├── pages/
└── App.tsx

コンポーネントを分ける基準

行数ではありません。役割が2つ以上になったら分けます。

分けどきのサイン
役割が2つ以上ある1つのファイルがデータ取得も一覧もフォームも持っている
名前が付けられる「これは検索ボックスだ」と言えるまとまり
同じ形を2回書いたコピペしたくなった瞬間
中で状態を持ち始めた開閉・選択などを自分で持つようになった

逆に、分けないほうがいい場合もあります。

  • 1回しか使わない数行——ファイルを開く手間のほうが大きい
  • 親子でpropsを10個受け渡す——分け方が間違っているサイン。まとまりを見直す

命名の実務的な線

こまかい話ですが、レビューで指摘されるのはたいていここです。

対象慣習
コンポーネントのファイルPascalCase.tsxBirdCard.tsx
フックuseXxx.tsuseBirds.ts
その他の関数・定数camelCase.ts
イベント系のpropsonXxxonToggleFavorite
真偽値isXxx / hasXxxisFavorite
ハンドラ関数handleXxxhandleSubmit

on〜は「渡されるもの」、handle〜は「実装するもの」という使い分けが定着しています。守るとコードを読む人の予測が当たるようになります。

LintとFormatterは入れておく

チーム開発では前提と考えてよいものです。

  • Formatter(Prettierなど)——書式を自動でそろえる。インデントや引用符の指摘がレビューから消える
  • Lint(ESLint / Oxlintなど)——バグになりやすい書き方を検出する。Hooksのルール違反もここで拾える

ViteのreactテンプレートにはOxlintが最初から入っています(npm run lint)。ESLintを使いたい場合は、作るときに--eslintを付けるか、あとから差しかえます。

べんりワザクラス名の付け方に迷わなくなる考え方命名の考え方そのもの(CSSのクラス名の話ですが、判断の軸は同じです)

レビューで実際に指摘されること

最後に、新人のPRでよく付く指摘を挙げておきます。ここまでのコースで全部扱った内容です。

指摘このコースのどこか
keyindexを使っているlistsの回
計算できる値をstateで持っているeventsの回
useEffectでやらなくていいことをしているeffectの回
propsが多すぎる/関係ないものが混ざっているconditionalの回
状態の置き場所が高すぎる/低すぎるlifting-stateの回
anyで押し切っているtypescript-practiceの回
ボタンにラベルが無い(読み上げできない)stateの回

「動くけれど指摘される」ものの正体は、ほとんどが置き場所の問題です。

このレッスンのまとめ

  1. 種類ごとで始めて、つらくなったら機能ごと(features/)へ。最初から切らない
  2. 機能ごとの強みは変更が1フォルダで完結すること。消すときも楽
  3. 分けどきは行数ではなく役割が2つ以上になったとき
  4. 細かく割りすぎない。1画面を読むのに5ファイル開くのは分けすぎ
  5. 命名はPascalCase.tsx / useXxx / onXxx(渡す) / handleXxx(実装する)
  6. LintとFormatterは入れる——書式の指摘をレビューから消すため
  7. レビューの指摘の多くは「置き場所」の問題。このコースで扱ってきたものばかり

次は、古いReactのコードを読む回です。既存の案件に入ると必ず当たります。

やってみよう:自分のプロジェクトを見直す

新しく作るものはありません。いま手元にあることりずかんを読み返します

  1. src/の中を眺めて、いま何がどこにあるかを書き出す
  2. BirdListPageを読んで、役割がいくつあるか数える(絞りこみ・フォーム・一覧…)
  3. 分けるとしたらどこか、分けないほうがいいのはどこかを考える
  4. npm run lintを実行して、指摘が出るか確認する
  5. 余力があれば、features/birds/を作って鳥まわりを移してみる
npm run lint

3番に正解はありません。「なぜここで分けるのか」を自分の言葉で言えるかが、この回で持ち帰ってほしいものです。実務のレビューで問われるのも、たいていそこです。

よくある質問

Reactにディレクトリ構成の決まりはありますか?
公式に決まった形はありません。小さいうちは種類ごと(components・hooks・pages)で足り、大きくなったら機能ごと(features)にまとめ直すのが一般的な流れです。
コンポーネントはどれくらいの大きさで分けますか?
行数より、役割が2つ以上あるかで判断します。1つのファイルがデータ取得も一覧表示もフォームも持ち始めたら分けどきです。名前が付けられるかどうかも良い目安になります。
1ファイルにコンポーネントを複数書いてもいいですか?
そのファイルの中だけで使う小さな部品なら問題ありません。他のファイルから使うものはファイルを分け、1ファイル1エクスポートにしておくと探しやすくなります。
LintとFormatterは必要ですか?
チーム開発では前提と考えてよいものです。書式の指摘がレビューから消え、内容の議論に集中できます。個人開発でも入れておくと、あとでチームに入るときに困りません。