― 分けすぎても、まとめすぎても壊れる理由 ―
※ChatGPTを使用して記事を作成しています。
Compose を書いていると、必ず迷う瞬間があります。
- この UI は関数を分けるべき?
- 1画面1Composableで十分?
- 小さく分割するのが正義?
- パフォーマンス的に不利では?
そして気づくと、
- 1,000行の巨大Composable
- 50個に分割された謎の部品群
のどちらかに転びます。
この記事では、
- なぜ分割が必要なのか
- どこまで分けるのが妥当か
- 分けすぎると何が起きるか
を整理します。
まず結論
Composable は「見た目」ではなく
責務単位で分割する
これが原則です。
失敗例①:巨大Composable症候群
@Composable
fun ProfileScreen(state: UiState) {
Column {
TopBar(...)
if (state.isLoading) {
Loading()
} else {
UserHeader(state.user)
StatsSection(state.stats)
PostList(state.posts)
Footer(...)
}
if (state.showDialog) {
ConfirmDialog(...)
}
}
}
最初は問題ありません。
しかし機能追加が続くと、
- 条件分岐が増える
- 表示ロジックが増える
- Stateの参照箇所が散らばる
やがて、修正が怖いコードになります。
なぜ巨大Composableは危険なのか
① 再利用できない
UIの一部だけを別画面で使えない。
② 責務が混在する
- ヘッダー表示
- リスト表示
- ダイアログ制御
が1つの関数に存在する。
③ テスト不能
Previewもしづらい。
失敗例②:分割しすぎ問題
逆にこうなるケースもあります。
@Composable
fun TitleText(text: String)
@Composable
fun DescriptionText(text: String)
@Composable
fun DividerLine()
@Composable
fun IconWrapper(icon: ImageVector)
一見きれいですが、
- 意味のない分割
- 文脈が見えない
- 呼び出し側が複雑
になります。
これは「見た目単位」で分割してしまった例です。
分割の正しい基準
基準①:責務が独立しているか?
例えば、
- ヘッダー部分
- 投稿一覧部分
- ダイアログ部分
これらは機能的に独立しています。
@Composable
fun ProfileHeader(user: UserUiModel)
@Composable
fun PostSection(posts: List<PostUiModel>)
@Composable
fun DeleteDialog(...)
この分割は意味があります。
基準②:Stateの関心が違うか?
Stateの粒度とComposableの粒度は連動します。
例:
- formState は FormSection に渡す
- listState は ListSection に渡す
巨大な UiState を丸ごと渡すのではなく、
FormSection(state.formState)
ListSection(state.listState)
のように分けます。
基準③:単独で Preview できるか?
良いComposableは、
- 引数だけで成立する
- 外部依存がない
- ViewModelを知らない
という特徴があります。
再コンポーズとの誤解
よくある不安:
分割すると再コンポーズが増えませんか?
結論:
分割したほうが再評価範囲はむしろ小さくなることが多い
Composeは差分計算します。
小さく責務を分けたほうが、
- 変更箇所が局所化
- スキップ判定が効きやすい
です。
「Stateを持つComposable」はどこまで許すか
原則はシンプルです。
① 画面全体のState → ViewModel
② 局所的なUI状態 → remember
例:
@Composable
fun ExpandableCard(content: String) {
var expanded by remember { mutableStateOf(false) }
}
これは問題ありません。
しかし、画面全体に影響する状態を内部に閉じ込めると破綻します。
実務での分割テンプレート
- Screen(ViewModel接続)
- Section(責務単位)
- Component(UI部品)
例:
ProfileScreen
├── ProfileHeader
├── StatsSection
├── PostSection
│ ├── PostItem
│ └── ...
└── ConfirmDialog
この階層は自然で読みやすい構造です。
分割のしすぎを防ぐ質問
- それは意味単位か?
- 別の画面で再利用する可能性があるか?
- Stateの関心は独立しているか?
- テストやPreviewが楽になるか?
YESが少ないなら分割不要です。
まとめ
Composable 分割の本質は、
UIを小さくすることではない
責務を明確にすること
です。
- 巨大Composable → 責務混在で破綻
- 過剰分割 → 文脈崩壊で可読性低下
ちょうどよい分割は、
- 状態の粒度と一致し
- 関心ごとが分離され
- 単体で成立する
Composable です。

