はじめに

React 開発では 「名前は軽快でも中身は巨大」 ― そんなコンポーネントに出会うことが珍しくありません。
レビューアーは 命名と実装責務の乖離 を早期に検出しなければ、後続開発者の認知負荷を増大させます。本稿では、命名と責務がずれている兆候 を整理し、再設計の観点と提案方法をまとめます。
検索キーワード:React コンポーネント 責務分離, 命名 規約, ファットコンポーネント リファクタリング

なぜ命名と責務がずれるのか

  • 初期スコープの肥大化
    MVP フェーズで仮配置したロジックが段階的に膨張し、命名更新が置き去りになる。
  • 業務ドメインの曖昧さ
    ビジネス用語と UI 用語が混在し、適切な抽象度が見えなくなる。
  • レビュー観点の不足
    「ファイルサイズ」「行数」の定量的指標だけを注視し、名前と内部構造の意味的整合 を検査しない。
用語解説:ファットコンポーネントとは

「単一責任の原則」を満たさず、多数の状態・副作用・イベント処理を抱えた巨大なコンポーネントの俗称です。可読性・再利用性・テスト容易性が損なわれる傾向にあります。

兆候チェックリスト

  • ファイル名と export 名が UI 意図を示さない
    例:Dashboard.jsx が内部でモーダルや REST 呼び出しまで担う。
  • useEffect が複数の無関連副作用を処理
  • Props が10項目を超え、プレーンオブジェクトでバケツリレー
  • ハンドラ名が handleClick* 系で乱立し、ユースケースが読めない
  • 他レイヤー(API / Router / i18n)への直接依存が3種以上

レビュー観点 1:命名の粒度と責務の照合

ダッシュボードコンポーネント(初版)
export function Dashboard() {
  const [users, setUsers] = useState<User[]>([]);
  const [open, setOpen] = useState(false);

  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(setUsers);
@Reviewer
REST 呼び出しは別 Hook へ抽出し、UI から切り離す方が読みやすいです
}, []); const handleSubmit = (payload: NewUser) => { fetch('/api/users', { method: 'POST', body: JSON.stringify(payload) }) .then(() => setOpen(false));
@Reviewer
「Dashboard」という名前から POST 処理は連想しづらいです。責務が混在しています
}; return (/* 略 */); }
  • 診断
    • コンポーネント名から ユーザ追加ロジック を想像しづらい
    • useEffecthandleSubmit がネットワーク境界を直接操作
    • 命名を変えるべきか、責務を分割すべきか両面検討

レビュー観点 2:命名再考 or 責務分離の判断基準

  1. UI レイヤーが唯一の責務 → 名前を UI コンポーネント視点に統一
  2. 副作用・データ取得・状態計算を同居 → 専用 Hook / Container で分離
  3. 機能単位のまとまりが 300 行超 → ファイル物理分割 + 名前の再定義

再設計パターン

パターンA:Custom Hook へ副作用を移譲

副作用切り出し例
function useUsers() {
  const [users, setUsers] = useState<User[]>([]);
  const fetchUsers = async () => { /* 略 */ };

  useEffect(() => { fetchUsers(); }, []);
  return { users, fetchUsers };
}

export function UserDashboard() {
  const { users, fetchUsers } = useUsers();
  // UI 描画に専念
}
  • 効果 : 命名が UserDashboard で UI 意図に集中
  • 残課題 : POST 処理が UI 内に残るかどうか

パターンB:Container / Presentational 分割

分割構成
src/
 ├ containers/
 │   └ UserDashboardContainer.tsx
 └ components/
     └ UserDashboard.tsx
  • コンテナは useUsersuseCreateUser を合成
  • 純粋 UI は props 経由で描画し、副作用を持たない

パターンC:ディレクトリごとの Bounded Context

業務ドメインが複合の場合、ディレクトリ単位で責務境界 を設ける。

src/
 └ user/
     ├ ui/
     │   └ Dashboard.tsx
     ├ hooks/
     │   ├ useUsers.ts
     │   └ useCreateUser.ts
     └ model/
         └ user.ts

UMLで可視化する依存関係

UML Diagram

ケーススタディ:命名と責務の乖離をレビューでどう指摘するか

リアルなPR抜粋
export function Analytics() {
  const [activeTab, setActiveTab] = useState<'daily' | 'monthly'>('daily');
  // REST, chart.js, i18n… 500行超
@Reviewer
- コンポーネント名に対して内部責務が多岐にわたります
- データ取得・チャート描画・翻訳切替は別層へ分割し
UI はタブ切替とプレゼンテーションだけに集中させましょう
@Developer
分割後の命名は `AnalyticsContainer` / `AnalyticsChart` で考えています
ご意見ください
}
  • レビューポイント
    1. 名前再考Analytics が広義すぎる場合は限定語を付す
    2. 責務抽出ChartRenderer, useAnalyticsData, TabState 等へ分割
    3. テスト戦略:分割により単体テストが可能になりカバレッジ向上

エビデンスと参考資料

  • React公式ブログ「A Complete Guide to Component Design」(2024-11-12 公開)
  • GitHub Octoverse 2024 Report 「Top React Antipatterns」
  • Nielsen Norman Group UX Research 「Component Naming and Cognitive Load」(調査番号 NN-UX-22-C013)

まとめ

命名と責務の乖離は コード迷子 を招き、保守コストを跳ね上げます。
レビューアーは「名前→責務→依存」の三段階で照合し、分割と命名の両輪 で再設計を提案することが望ましいです。まずは Custom Hook 抽出や Container 分離といった 低コストなリファクタ から着手し、コンポーネントの意図を名前に正しく映し出す構造を目指しましょう。