Jetpack ComposeのComposable内でMutableStateを宣言する場合、大きく3通りの記述方法があります。
val mutableState = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
公式ドキュメントによれば「機能は同じだから状況に応じて読みやすいものを使ってね」とのことですが、実際のところ、どう使い分けるべきでしょうか?
結論
- 基本的にはdelegated propertiesを使うのが良さそう
var value by remember { mutableStateOf(default) }
- state hoisting等でsetterを引数に渡したいときはdestructuring declarationsを使う
val (value, setValue) = remember { mutableStateOf(default) }
考察
まず、用途が似ているのは次の2つだと思います。
val mutableState = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
記法としては前者が.valueによってアクセスする一方、後者はシンプルです。
// Read
println(mutableState.value)
println(value)
// Mutation
mutableState.value = someValue
value = someValue
従って、基本的にはdelegated propertiesを使うのが良いでしょう。
Composeの公式サンプルでも基本的にはdelegated propertiesを使用しているようです。
前者はいつ使うのか?という話ですが、valueではなくオブジェクトそのもの(State/MutableState)にアクセスしたい場合には前者の記法になりそうです。
次にsetValueを同時に宣言するパターンです。
val (value, setValue) = remember { mutableStateOf(default) }
明らかな違いはsetValueが変数として宣言されている点です。
これは他のComposableに実引数として渡すときに便利です。
val (value, onValueChanged) = remember { mutableStateOf(default) }
SomeComposable(value, onValueChanged)
これはstate hoistingの際に使うことが多そうです。公式サンプルでもそういったユースケースで使われています。
その他: なぜ3通りの書き方で動くのか?
var value by remember { mutableStateOf(default) }
はKotlinのdelegated propertiesによって実現されています。
内部的にはState<T>.getValueとMutableState<T>.setValueを実行しています。従って次のimport文が必要になるわけですね。
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
そして、
val (value, setValue) = remember { mutableStateOf(default) }
はKotlinのdestructuring declarationsによって実現されています。
実際にMutableStateの定義を確認すると、component1及びcomponent2が確認できると思います。
@Stable
interface MutableState<T> : State<T> {
override var value: T
operator fun component1(): T
operator fun component2(): (T) -> Unit
}