第4回:remember と rememberSaveable の正しい使い分け ― 若手が一番つまずく状態保持の本質

※ChatGPTを使用して記事を作成しています。

Jetpack Compose を学び始めたばかりの人が必ず抱える悩みがあります。それは――
「なんで状態が消えるの?」
この問題のほとんどは、remember と rememberSaveable の仕組みを正しく理解していないことが原因です。

しかしこの2つの違いは、単なる「保存されるかどうか」の差ではありません。
重要なのは、それぞれの“寿命”がどこまで及ぶのか、そして どんな種類の状態を保持すべきなのか という設計の本質です。

今回は、若手が最もつまずく“状態の寿命管理”を、再現コードと具体例を交えながら徹底的に整理していきます。

 

第1章 まず「状態の寿命」を理解しよう

Compose の状態管理は、次の3つのレイヤーで考えると理解しやすくなります。

① Composition(UI の再描画サイクル)

  • Composable の再描画単位
  • remember はこのスコープに従う

つまり、
Composable が破棄されて再生成されれば remember の値は消える
ということです。

② Activity / ViewModel の寿命

  • 画面回転などで Activity が再生成されても
  • ViewModel は再生成されない

rememberSaveable はこの「画面再生成」に対応します。

③ プロセス寿命(OSによるKill)

  • アプリがバックグラウンドで殺されても
  • 復帰したら状態を復元してほしい

rememberSaveable はここまで耐えることがあります(型・Saverに依存)。

まとめるとこうです:

機能Composition破棄で消えるActivity再生成で消えるプロセスKillで消える
remember✔ 消える✔ 消える✔ 消える
rememberSaveable✔ 消える✖ 残る✖ or ✔(型による)

 

第2章 若手がハマる典型例:画面回転で入力値が消える

まずは、現場でよく見る “あーこれよくあるね” というやつから。

よくある失敗コード

@Composable
fun InputScreen() {
    var name by remember { mutableStateOf("") }

    TextField(
        value = name,
        onValueChange = { name = it }
    )
}

現象

  • 入力して
  • 端末を回転すると
  • name がリセットされて空になる

若手エンジニアが必ず言うセリフ:

「remember に入れてるのに、なんで消えるの?」

理由:remember は Composition の寿命しか持たない

画面回転すると Activity が再生成されます。
すると Composable もすべて再実行され、
remember が初期化されます。

つまり remember の寿命は「画面回転」に耐えられないのです。

第3章 正しくは rememberSaveable を使う

正しいコードはこちら。

@Composable
fun InputScreen() {
    var name by rememberSaveable { mutableStateOf("") }

    TextField(
        value = name,
        onValueChange = { name = it }
    )
}

これで入力値は画面回転でも残る

rememberSaveable は内部的に以下の仕組みを使って値を保持します:

  • SavedStateHandle
  • Bundle(型によっては自動保存)
  • Saver が定義されていればそれを利用

第4章 rememberSaveable が万能ではない理由

若手がよくやる間違い:

「なんでも rememberSaveable にすればいいんでしょ?」

これは完全に誤りです。

rememberSaveable が保存できるデータには制限があります。

① 保存可能な型(Bundle の制約)

Bundle に保存できる型は限られています。

  • String
  • Int
  • Boolean
  • List<String>
  • Parcelable
  • Serializable
  • … など

複雑なデータ(Repository、Context、Bitmapなど)は保存できません。

② 保存すべきではない“UI 状態じゃないデータ”

例えば次は絶対に rememberSaveable に入れてはいけません。

  • Repository
  • ViewModel
  • RetrofitのAPIクライアント
  • 大量データ(数百KB以上)

これらを保存すると別の問題を引き起こします。

第5章 remember を使う場面の本質は“UI 状態のみ”

remember で保持すべきなのは以下のような性質のデータです:

① UI 内部の一時的な状態

  • ボタンの押された回数
  • UI の展開・折りたたみ状態
  • スクロール位置
  • 一時的なエラーメッセージ表示フラグ
var expanded by remember { mutableStateOf(false) }

画面回転でリセットされても問題ない類の状態です。

② Composition の間だけ存在すべき情報

例えばアニメーション。

val alpha by animateFloatAsState(targetValue)

アニメーションの状態を端末回転で維持したいシーンはほぼありません。

③ ViewModel に渡す必要がない状態

remember の本質はこれです。

「UI の中だけ、短い寿命で完結する状態」

これに当てはまるものは remember が最適です。

第6章 rememberSaveable を使うべき状態の特徴

逆に rememberSaveable を使うべきなのは…

① ユーザーが時間をかけて入力する情報

  • 名前入力
  • メールアドレス
  • 検索フォームの値
  • フィルタリング条件

端末回転で消えてほしくないものです。

② UIの表示選択が「ユーザーの意図」に紐づくもの

  • タブ選択
  • ページ選択状態
  • 表示モード(一覧 or グリッド)

③ ViewModelで管理するほど重くない状態

rememberSaveable は UIスコープで完結できるため、ViewModel に不要な責務を押しつけずに済みます。

第7章:remember と rememberSaveable の使い分け判断基準

簡単な判断フローを用意しました。

① 画面回転で“残すべき”情報?

YES:rememberSaveable
NO:remember

② 状態の寿命は UI の中だけで完結する?

YES:remember
NO(ユーザー意図に紐づく):rememberSaveable

③ ViewModel に移すほどではない?

YES:rememberSaveable
NO:ViewModelへ移管

④ 保存できる型か?(Bundle制約に入る?)

YES:rememberSaveable
NO:Saver を定義する or ViewModelへ移管

第8章 Saver を使って複雑な型を rememberSaveable で保持する

例えばカスタムデータクラスを保存したい場合。

data class UserInput(
    val name: String,
    val age: Int
)

Bundle に保存できない場合、Saver を定義すれば使えます。

Saver の例

val UserInputSaver = run {
    Saver<UserInput, List<Any>>(
        save = { listOf(it.name, it.age) },
        restore = {
            UserInput(
                name = it[0] as String,
                age = it[1] as Int
            )
        }
    )
}

rememberSaveable + saver

var input by rememberSaveable(stateSaver = UserInputSaver) {
    mutableStateOf(UserInput("", 0))
}

これで複雑な型でも画面回転で保持できます。

終章:remember と rememberSaveable を理解すると、Compose の状態管理が“手のひらに乗る”

remember と rememberSaveable の違いは単に
「保存されるかどうか」
ではありません。

本質は 状態の寿命(lifecycle)の違い です。

  • UIが一時的に使うだけ → remember
  • ユーザーの意図に紐づく状態 → rememberSaveable
  • 画面再生成に耐える必要がある → rememberSaveable
  • 複雑な状態 → Saver or ViewModel

これを理解すると、Compose の状態管理が一気に安定し、
UI の挙動が“予測可能”になります。

そして、若手が陥る「状態が勝手に消える問題」から解放されます。

タイトルとURLをコピーしました