Compose state explained
日付:2021/08/12
URL:https://dev.to/zachklipp/scoped-recomposition-jetpack-compose-what-happens-when-state-changes-l78
調査者:Mori Atsushi
カテゴリ:Jetpack Compose
一言で表すと
概要
Scoped recomposition in Jetpack Compose — what happens when state changes?
code:kotlin
@Composable fun Foo() {
var text by remember { mutableStateOf("") }
Button(onClick = { text = "$text\n$text" }) {
Text(text)
}
}
Buttonを押したときにRecomposeされるのは?
1. Foo
2. Button
3. Button's content lambda
4. Text
正解:3. Button's content lambda
以下のように考えればわかりやすい
code:kotlin
@Composable fun Foo() {
val text: MutableState<String> = remember { mutableStateOf("") }
Button(onClick = { text.value = "${text.value}\n${text.value}" }) {
val value = text.value
Text(value)
}
}
Composableをinline関数にすると展開されて、Recomposeのscopeも大きくなるので注意
code:kotlin
@Composable fun App() {
println("App recomposing")
Wrapper {
println("Lambda recomposing")
// Read some state causing recomposition…
}
}
@Composable inline fun Wrapper(content: @Composable () -> Unit) {
println("Wrapper recomposing")
content()
}
副作用を持たせない設計をしていれば、どこがRecomposeされても問題ないので、そこまで気にしなくて良い
chigichan24.icon 描画のパフォーマンスが気になるレベルの話ではない?
remember { mutableStateOf() } – A cheat sheet
code:kotlin
var text by remember { mutableStateOf("") }
Property delegation
var foo: T by bar
(当然だが) Kotlinの言語仕様でComposeは関係ない
特別なgetterとsetterを持つ
fun getFoo(): T
fun setFoo(value: T)
remember
渡した関数(ラムダ)から返された値を「メモ化」してからその値を返すComposable関数
1. 初めてrememberが実行されると、ラムダを実行して値を取得する
2. 2回目以降は以前の値を返す
mutableStateOf
valueが書き換わったときにsnapshot observersに通知する
mutableStateOfはCompose外でも使える、rememberはCompose内でのみ使える
code:kotlin
class MyFieldState() {
var text by mutableStateOf("")
}
@Composable fun MyField() {
val state = remember { MyFieldState() }
TextField(state.text, { state.text = it })
}
reactive stateモデルの歴史的紹介
UIの更新は難しい
命令的UI
updateTextを呼び忘れてはいけない
mayamito.icon notifyDataSetChanged ...
code:kotlin
class CounterButton(val counter: Counter) : Button() {
fun initialize() {
updateText()
setOnClickListener {
counter.increment()
updateText()
}
setOnLongClickListener {
counter.decrement()
updateText()
}
}
private fun updateText() {
this.text = “Counter: ${counter.value}”
}
}
RxJava / LiveData / Flow
リークを考慮する必要がある
ボイラープレートが多い
code:kotlin
class Counter {
private val _value = BehaviorSubject.createDefault(0)
val value: Observable<Int> = _value
fun increment() { _value.value++ }
fun decrement() { _value.value— }
}
class CounterButton(val counter: Counter) : Button() {
fun initialize() {
counter.value.subscribe { value ->
this.text = “Counter: $value”
}
setOnClickListener {
counter.increment()
}
setOnLongClickListener {
counter.decrement()
}
}
}
State(Compose)
code:kotlin
class Counter {
var value: Int by mutableStateOf(0)
private set
fun increment() { value++ }
fun decrement() { value— }
var label: String by mutableStateOf(“”)
}
class CounterButton(val counter: Counter) : Button() {
fun initialize() {
this.text = “${counter.label}: ${counter.value}”
setOnClickListener {
counter.increment()
}
setOnLongClickListener {
counter.decrement()
}
}
}
こうすれば動く(?)
code:kotlin
snapshotFlow { initialize() }
.launchIn(scope)
chigichan24.icon snapShotFlow、おもろそう
スレッドセーフ
Stateはどのスレッドから書き換えても安全
mayamito.icon LiveDataより使い勝手よさそう?
Mori Atsushi.icon 値取得して書き換える場合は?
Introduction to the Compose Snapshot system
Stateは以下によって作成ができる
mutableStateOf/MutableState
mutableStateListOf/SnapshotStateList
mutableStateMapOf/SnapshotStateMap
derivedStateOf
rememberUpdatedState
collect*AsState
気になるポイント
ViewModelやそれ以下のレイヤーでComposeのStateを使うかいなか
コメント