コンポーネント
コンポーネントは、見た目の断片ではなく、利用者が理解できる役割と状態の境界で分けます。
境界の決め方
次のいずれかが独立する場合に分割します。
- 利用者から見た役割や操作
- データの取得契約
- loading、empty、errorの境界
- 再利用やテストの単位
- アクセシビリティ上まとまったUI
行数だけで分割せず、小さなラッパーを増やして処理の流れを隠しません。名前にはCardやWrapperだけでなく、InvoiceSummaryやMemberSearchのような画面上・業務上の意味を使います。
UIコンポーネント
shadcn/uiを採用しているプロジェクトでは、追加されたソースをプロジェクト所有の基盤として編集します。カスタマイズのためだけに別ディレクトリへ複製せず、共通のvariantとアクセシビリティを元のコンポーネントへ反映します。
画面を組み立てるコンポーネントは、基盤UIをcompositionして業務上の意味を与えます。スタイル差分だけのラッパーを無制限に増やしません。
データとの境界
データ契約は実際の利用側から追える場所に置きます。GraphQLを使う場合はFragmentをコンポーネントの近くへ、Hono RPCやRESTを使う場合は利用するendpointとqueryを近くへ置きます。特定の通信方式を全プロジェクトへ要求しません。
サーバーデータをpropsへ変換するときは、画面が必要とするDTOを明示し、データベース行や外部APIレスポンスをそのまま広く渡さないようにします。
必須の状態
成功表示だけでなく、次を同じ実装単位で確認します。
- 初回読み込みと再読み込み
- データがない状態
- 利用者が対処できるエラー
- 権限や処理中による無効状態
- キーボード操作、フォーカス、ラベル、読み上げ
StorybookやMSWを導入しているプロジェクトでは、実運用の制約を満たすfixtureとhandlerを共有します。表示だけを通す不正確なfixtureを量産しません。
パフォーマンス
React Compilerを採用しているプロジェクトでは、既定の最適化を前提にし、useMemoやuseCallbackを習慣で追加しません。Compilerを採用していない場合も、Profilerや実測で問題を確認してから、性能上の分割やメモ化を行います。
ライブラリ互換性や参照同一性の契約のためにメモ化が必要な場合は、その理由をコード上で局所化します。