UiStateを使った状態管理

Photo by Med Badr Chemmaoui on Unsplash

状態管理する方法は色々ありますが、UiStateという状態を一つのclassでまとめて管理する方法を紹介します。
(UiStateの他にはUiModel, ViewStateなど色んな呼び方を見かけます)

UiStateでも大きく2つやり方があるかなと思っています。
sealed class を使ったやり方と data class を使ったやり方です。

sealed class を使った管理

sealed class を使って画面が取り得る状態を定義して、必要なデータは各継承クラスのプロパティに持たせる方法です。

以下のコメントに書いてる通り、各状態を sealed class を継承した data classobject で表現しています。

ViewModelではこれを StateFlowLiveData を使って状態ごとに更新していきます。

Activity/Fragment ではこの変更を受け取れるように実装していきます。

このやり方はAndroid Developerのドキュメントでもやっていたりします。

https://developer.android.com/kotlin/flow/stateflow-and-sharedflow

ぼく個人としてはこのやり方はいくつか問題点があり、あまりやりたくはない方法になります。

上の例で示したとおり、画面全体の状態を表しているので、ちょっと複雑な画面などすぐに破綻します。
例えば、画面にお気に入りボタンがあったりした場合などは、 sealed class の継承するものがどんどん増えていきます。

画面の更新についても、画面全体の状態と表すのでUIを更新する際に冗長になり、またミスが増え要因になるように思えます。
以下に例を示しますが、画面全体の状態ごとにすべてのUIの状態を設定する必要があります。

また、DataBindingをシンプルに使用することができません。

data class を使った状態管理

次に単純な data class を使って管理する方法です。

sealed class のように画面全体の表現するのではなく、UIを表現するのに必要なものをプロパティとして定義するイメージです。

ViewModelは以下のように StateFlowLiveData を使いつつ copy メソッドで変更したいプロパティのみを更新していきます。

value を参照して更新する箇所が微妙な感じですが、これは以下のような拡張関数を作っておくことで多少シンプルになります。

Activity/Fragment側は以下のようになります。

分岐もなくUiStateの状態を素直にUIに反映するだけです。この例ではViewBindingを使っていますが、DataBindingでも問題なく使用できます。

画面のUIが増えたとしても変更は容易かと思います。

状態の組み合わせでUIの状態を表したいときなどは、フィールド等を追加すればActivity/Fragmentで分岐したり、DataBinding内でいくつも条件を記述する必要もないです。

data class を使った状態管理ではそこまで大きな問題はないと思ってますが、インスタンスを何度も生成するのでパフォーマンスが気になるかもしれません。

ぼくが触ってる感じでは大きくパフォーマンスが変化してるふうには見えないのでそこまで問題にはならないかなと思いますが、一応頭にいれておくと良いかもです。

Composeの場合

Composeの状態管理ですが、ぼく自身まだ明確な回答を持ってるわけではないです。上で紹介した sealed classdata classcollectAsState などを使えばそのまま使えたりしますが、もっと良い方法があるかもしれません。

Composeについてはこれから色々試していきます。

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store