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
}