Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Jetpack Compose の Side-effect を使いこなす / DroidKai...
Search
star_zero
September 15, 2023
Programming
5
5.5k
Jetpack Compose の Side-effect を使いこなす / DroidKaigi 2023
star_zero
September 15, 2023
Tweet
Share
More Decks by star_zero
See All by star_zero
今からはじめるAndroidアプリ開発 2024 / DevFest 2024
star_zero
0
1k
Android 14 新機能 / Android 14 Meetup Nagoya
star_zero
1
560
Android 14 と Predictive back gesture / Shibuya.apk #42
star_zero
0
350
Coroutines Test 入門 / Android Test Night #8
star_zero
2
1k
What's new in Jetpack / I/O Extended Japan 2022
star_zero
1
620
Kotlin 2021 Recap / DevFest 2021
star_zero
3
1.2k
Kotlin Symbol Processing (KSP) を使ったコード生成 / DroidKaigi 2021
star_zero
2
5.1k
What's new Android 12
star_zero
0
550
これからはじめるAndroid開発 / DevFest 2020
star_zero
4
680
Other Decks in Programming
See All in Programming
Асинхронность неизбежна: как мы проектировали сервис уведомлений
lamodatech
0
600
コンテナをたくさん詰め込んだシステムとランタイムの変化
makihiro
1
120
テスト自動化失敗から再挑戦しチームにオーナーシップを委譲した話/STAC2024 macho
ma_cho29
1
1.3k
ソフトウェアの振る舞いに着目し 複雑な要件の開発に立ち向かう
rickyban
0
890
フロントエンドのディレクトリ構成どうしてる? Feature-Sliced Design 導入体験談
osakatechlab
8
4.1k
Security_for_introducing_eBPF
kentatada
0
110
Semantic Kernelのネイティブプラグインで知識拡張をしてみる
tomokusaba
0
180
Refactor your code - refactor yourself
xosofox
1
250
今年一番支援させていただいたのは認証系サービスでした
satoshi256kbyte
1
250
「Chatwork」Android版アプリを 支える単体テストの現在
okuzawats
0
180
fs2-io を試してたらバグを見つけて直した話
chencmd
0
220
Scalaから始めるOpenFeature入門 / Scalaわいわい勉強会 #4
arthur1
1
300
Featured
See All Featured
jQuery: Nuts, Bolts and Bling
dougneiner
61
7.5k
Writing Fast Ruby
sferik
628
61k
Music & Morning Musume
bryan
46
6.2k
Producing Creativity
orderedlist
PRO
341
39k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
251
21k
GitHub's CSS Performance
jonrohan
1030
460k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
132
33k
How STYLIGHT went responsive
nonsquared
95
5.2k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
6.9k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
Building Your Own Lightsaber
phodgson
103
6.1k
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
Transcript
Jetpack Compose の Side-effect を使いこなす Kenji Abe - 2023/09/15
自己紹介 • Kenji Abe • Google Developers Expert for Android,
Kotlin • DeNA Co., Ltd. • X: @STAR_ZERO • Bluesky: @star-zero.com 2
Side-effect? (副作用) 3
Side effect (副作用) • 関数や操作などが結果を返すなどの主となる効果以外の効果 ◦ グローバル変数の変更 ◦ I/O操作 •
ほかのSide effectを起こす関数の呼び出し • 実行順によって結果が変わる可能性がある • デバッグが困難になる • テストが難しくなる 4
Composeにおける Side effect 5
副作用とは、 コンポーズ可能な関数の範囲外で 発生するアプリの状態の変化を 指します。 6 https://developer.android.com/jetpack/compose/side-effects?hl=ja
ComposeにおけるSide effect • Composable関数はSide effectがないようにすることが理想 • 予測できないRecomposition ◦ 実行順 ◦
スキップ ◦ 破棄 7
Side effectが必要な状況 8
Side effectが必要な状況 • スナックバーを表示する • 1 回限りのイベントをトリガーする • 特定の状態で別の画面に移動する •
などなど 9 https://developer.android.com/jetpack/compose/side-effects?hl=ja
Side effectが必要な状況 • スナックバーを表示する • 1 回限りのイベントをトリガーする • 特定の状態で別の画面に移動する •
などなど 10 https://developer.android.com/jetpack/compose/side-effects?hl=ja Side effectを安全に 実行する必要がある
Side effect APIs • LaunchedEffect • rememberCoroutineScope • DisposableEffect •
rememberUpdatedState • SideEffect • derivedStateOf • produceState • snapshotFlow 11 https://developer.android.com/jetpack/compose/side-effects?hl=ja
LaunchedEffect 12
LauchedEffect var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) {
if (showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } }
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect 後述
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect Coroutines
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect Snackbarを表示
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect フラグを戻して、次回も表示できるように
LaunchedEffectの 引数について 19
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect Key: 値が変わったときに起動される
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect false 1
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect false 1 初回は必ず起動する 2
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect Recomposition
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect false のまま 3
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect false のまま 3 Key が変更されてないので 実行されない 4
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect showSnackbar = true
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect false → true 5
var showSnackbar by remember { mutableStateOf(false) } LaunchedEffect(showSnackbar) { if
(showSnackbar) { snackbarHostState.showSnackbar( message = "OK" ) showSnackbar = false } } LauchedEffect false → true 5 Key が変更されたので 実行される 6
いろいろなKey 29
LaunchedEffect(Unit) { // ... } LauchedEffect ずっと同じ値 最初の1回だけ実行
LaunchedEffect(key1, key2, key3) { // ... } LauchedEffect 複数のKey どれかが変わったら実行する
LaunchedEffect のCoroutines 32
LaunchedEffectのCoroutines • キャンセルするタイミング ◦ keyが変わってLauchedEffectが再起動するとき ◦ CompositionからLeaveするとき 33 https://developer.android.com/jetpack/compose/lifecycle
LaunchedEffect(key) { delay(5000) Timber.d("OK") } LauchedEffect
LaunchedEffect(key) { delay(5000) Timber.d("OK") } LauchedEffect 5秒経過前にkeyが変わると ログが出力されずに最初から数え直し
LaunchedEffect(key) { delay(5000) Timber.d("OK") } LauchedEffect 5秒経過前にCompositionからLeaveすると ログが出力されずに終了
rememberCoroutineScope 37
rememberCoroutineScope val scope = rememberCoroutineScope() Button( onClick = { scope.launch
{ snackbarHostState.showSnackbar("Hello, World") } } ) { Text(text = "Show Snackbar") }
val scope = rememberCoroutineScope() Button( onClick = { scope.launch {
snackbarHostState.showSnackbar("Hello, World") } } ) { Text(text = "Show Snackbar") } rememberCoroutineScope CoroutinesScope取得
val scope = rememberCoroutineScope() Button( onClick = { scope.launch {
snackbarHostState.showSnackbar("Hello, World") } } ) { Text(text = "Show Snackbar") } rememberCoroutineScope ScopeからCoroutines起動
val scope = rememberCoroutineScope() Button( onClick = { scope.launch {
snackbarHostState.showSnackbar("Hello, World") } } ) { Text(text = "Show Snackbar") } rememberCoroutineScope Composition から Leave するときにキャンセル
DisposableEffect 45
DisposableEffect val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { val observer =
LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_START) { // ... } } lifecycleOwner.lifecycle.addObserver(observer) onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } }
val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver
{ _, event -> if (event == Lifecycle.Event.ON_START) { // ... } } lifecycleOwner.lifecycle.addObserver(observer) onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } DisposableEffect LifecycleOwner取得
val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver
{ _, event -> if (event == Lifecycle.Event.ON_START) { // ... } } lifecycleOwner.lifecycle.addObserver(observer) onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } DisposableEffect 引数は LauchedEffect と同じ感じ
val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver
{ _, event -> if (event == Lifecycle.Event.ON_START) { // ... } } lifecycleOwner.lifecycle.addObserver(observer) onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } DisposableEffect Coroutinesじゃない
val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver
{ _, event -> if (event == Lifecycle.Event.ON_START) { // ... } } lifecycleOwner.lifecycle.addObserver(observer) onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } DisposableEffect Lifecycle監視の処理
val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver
{ _, event -> if (event == Lifecycle.Event.ON_START) { // ... } } lifecycleOwner.lifecycle.addObserver(observer) onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } DisposableEffect クリーンアップの処理
DisposableEffect の処理の流れ 52
DisposableEffect var flag by remember { mutableStateOf(false) } DisposableEffect(flag) {
// ... onDispose { // ... } }
var flag by remember { mutableStateOf(false) } DisposableEffect(flag) { //
... onDispose { // ... } } DisposableEffect false 1
var flag by remember { mutableStateOf(false) } DisposableEffect(flag) { //
... onDispose { // ... } } DisposableEffect false 1 実行 2
var flag by remember { mutableStateOf(false) } DisposableEffect(flag) { //
... onDispose { // ... } } DisposableEffect false → true 3
var flag by remember { mutableStateOf(false) } DisposableEffect(flag) { //
... onDispose { // ... } } DisposableEffect 実行 4
var flag by remember { mutableStateOf(false) } DisposableEffect(flag) { //
... onDispose { // ... } } DisposableEffect 実行 5
59 LaunchedEffect DisposableEffect • クリーンアップできない • Coroutinesで起動 ◦ 若干のオーバーヘッド •
onDisposeによるクリーンアップ • Coroutinesではない
rememberUpdatedState 60
rememberUpdatedState を使わなかった場合 61
var count by remember { mutableIntStateOf(0) } Button(onClick = {
count++ }) { Text(text = "Increment: $count") } Content(count = count) rememberUpdatedState
var count by remember { mutableIntStateOf(0) } Button(onClick = {
count++ }) { Text(text = "Increment: $count") } Content(count = count) rememberUpdatedState 単純なインクリメント処理
var count by remember { mutableIntStateOf(0) } Button(onClick = {
count++ }) { Text(text = "Increment: $count") } Content(count = count) rememberUpdatedState 別のComposable関数へ渡す
@Composable private fun Content(count: Int) { LaunchedEffect(Unit) { delay(5000) Timber.d("Count
= $count") } } rememberUpdatedState
@Composable private fun Content(count: Int) { LaunchedEffect(Unit) { delay(5000) Timber.d("Count
= $count") } } rememberUpdatedState 0, 1, 2 … と増えていく
@Composable private fun Content(count: Int) { LaunchedEffect(Unit) { delay(5000) Timber.d("Count
= $count") } } rememberUpdatedState 最初の1回だけ実行
@Composable private fun Content(count: Int) { LaunchedEffect(Unit) { delay(5000) Timber.d("Count
= $count") } } rememberUpdatedState 5秒後にログを表示
@Composable private fun Content(count: Int) { LaunchedEffect(Unit) { delay(5000) Timber.d("Count
= $count") } } rememberUpdatedState ???
@Composable private fun Content(count: Int) { LaunchedEffect(Unit) { delay(5000) Timber.d("Count
= $count") } } rememberUpdatedState Count = 0
@Composable private fun Content(count: Int) { LaunchedEffect(Unit) { delay(5000) Timber.d("Count
= $count") } } rememberUpdatedState Count = 0 最初の引数がキャプチャされるので 0 のまま
値が変わったら LauchedEffect を再起動する 72
@Composable private fun Content(count: Int) { LaunchedEffect(count) { delay(5000) Timber.d("Count
= $count") } } rememberUpdatedState
@Composable private fun Content(count: Int) { LaunchedEffect(count) { delay(5000) Timber.d("Count
= $count") } } rememberUpdatedState 値が変わったら再起動
@Composable private fun Content(count: Int) { LaunchedEffect(count) { delay(5000) Timber.d("Count
= $count") } } rememberUpdatedState 値が変わるたびに最初から数え直し
rememberUpdatedState を使う 76
@Composable private fun Content(count: Int) { val currentCount by rememberUpdatedState(count)
LaunchedEffect(Unit) { delay(5000) Timber.d("Count = $currentCount") } } rememberUpdatedState
@Composable private fun Content(count: Int) { val currentCount by rememberUpdatedState(count)
LaunchedEffect(Unit) { delay(5000) Timber.d("Count = $currentCount") } } rememberUpdatedState 引数の値を rememberUpdatedState で Stateに変換
@Composable private fun Content(count: Int) { val currentCount by rememberUpdatedState(count)
LaunchedEffect(Unit) { delay(5000) Timber.d("Count = $currentCount") } } rememberUpdatedState 変換した変数を参照
val currentCount = rememberUpdatedState(count) val currentCount = remember { mutableIntStateOf(count)
} currentCount.value = count rememberUpdatedState
rememberUpdatedState が必要かどうかの判断 81
rememberUpdatedStateが必要か検討するポイント • Composeの外で変化する状態を参照している • LauchedEffect, DisposableEffect の Key ではない状態を参照 •
時間が経過して使用される状態への参照 • コールバック関数 82
SideEffect 83
@Composable fun rememberAnalytics(user: User): FirebaseAnalytics { val analytics = remember
{ Firebase.analytics } SideEffect { analytics.setUserProperty("user_type", user.type) } return analytics } SideEffect
@Composable fun rememberAnalytics(user: User): FirebaseAnalytics { val analytics = remember
{ Firebase.analytics } SideEffect { analytics.setUserProperty("user_type", user.type) } return analytics } SideEffect
@Composable fun rememberAnalytics(user: User): FirebaseAnalytics { val analytics = remember
{ Firebase.analytics } SideEffect { analytics.setUserProperty("user_type", user.type) } return analytics } SideEffect
@Composable fun rememberAnalytics(user: User): FirebaseAnalytics { val analytics = remember
{ Firebase.analytics } SideEffect { analytics.setUserProperty("user_type", user.type) } return analytics } SideEffect Userの状態が更新 → Recomposition 1
@Composable fun rememberAnalytics(user: User): FirebaseAnalytics { val analytics = remember
{ Firebase.analytics } SideEffect { analytics.setUserProperty("user_type", user.type) } return analytics } SideEffect Userの状態が更新 → Recomposition Recomposition → SideEffect実行 1 2
SideEffect のメリット 89
@Composable fun rememberAnalytics(user: User): FirebaseAnalytics { val analytics = remember
{ Firebase.analytics } SideEffect { analytics.setUserProperty("user_type", user.type) } analytics.setUserProperty("user_type", user.type) return analytics } SideEffect これと同じ??
@Composable fun rememberAnalytics(user: User): FirebaseAnalytics { val analytics = remember
{ Firebase.analytics } SideEffect { analytics.setUserProperty("user_type", user.type) } analytics.setUserProperty("user_type", user.type) error("Error") return analytics } SideEffect エラー発生
@Composable fun rememberAnalytics(user: User): FirebaseAnalytics { val analytics = remember
{ Firebase.analytics } SideEffect { analytics.setUserProperty("user_type", user.type) } analytics.setUserProperty("user_type", user.type) error("Error") return analytics } SideEffect 実行されない 実行される
SideEffect とログ 93
val listState = rememberLazyListState() LazyColumn(state = listState) { // ...
} Timber.d("index = ${listState.firstVisibleItemIndex}") SideEffect
val listState = rememberLazyListState() LazyColumn(state = listState) { // ...
} Timber.d("index = ${listState.firstVisibleItemIndex}") SideEffect スクロールのたびに状態が変わる
val listState = rememberLazyListState() LazyColumn(state = listState) { // ...
} Timber.d("index = ${listState.firstVisibleItemIndex}") SideEffect スクロールのたびに状態が変わる 状態の参照 = Recomposition対象
val listState = rememberLazyListState() LazyColumn(state = listState) { // ...
} Timber.d("index = ${listState.firstVisibleItemIndex}") SideEffect スクロールのたびに状態が変わる 状態の参照 = Recomposition対象 スクロールのたびに Recomposition
val listState = rememberLazyListState() LazyColumn(state = listState) { // ...
} Timber.d("index = ${listState.firstVisibleItemIndex}") SideEffect { Timber.d("index = ${listState.firstVisibleItemIndex}") } SideEffect これが要因でRecompositionが発生しなくなる
derivedStateOf 99
Box { val listState = rememberLazyListState() LazyColumn(state = listState) {
/* ... */ } val showButton = listState.firstVisibleItemIndex > 0 if (showButton) { Button() { Text(text = "ScrollTop") } } } derivedStateOf
Box { val listState = rememberLazyListState() LazyColumn(state = listState) {
/* ... */ } val showButton = listState.firstVisibleItemIndex > 0 if (showButton) { Button() { Text(text = "ScrollTop") } } } derivedStateOf スクロールしている場合はボタンを表示
Box { val listState = rememberLazyListState() LazyColumn(state = listState) {
/* ... */ } val showButton = listState.firstVisibleItemIndex > 0 if (showButton) { Button() { Text(text = "ScrollTop") } } } derivedStateOf スクロールのたびに Recomposition スクロールのたびに状態が変わる
Box { val listState = rememberLazyListState() LazyColumn(state = listState) {
/* ... */ } val showButton = listState.firstVisibleItemIndex > 0 if (showButton) { Button() { Text(text = "ScrollTop") } } } derivedStateOf スクロールのたびに Recomposition スクロールのたびに状態が変わる ここが変わったときだけ Recompositionしてほしい
Box { // ... val showButton = listState.firstVisibleItemIndex > 0
val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } } // ... } derivedStateOf derivedStateOf で結果が変わった時に Recompositionするようにする
derivedStateOf を 使わないほうが良い場合 105
@Composable fun Content(friends: List<User>) { val friendsCount by remember {
derivedStateOf { friends.size } } } derivedStateOf
@Composable fun Content(friends: List<User>) { val friendsCount by remember {
derivedStateOf { friends.size } } } derivedStateOf サイズが変更されたら変数の値も更新
@Composable fun Content(friends: List<User>) { val friendsCount by remember {
derivedStateOf { friends.size } } val friendsCount = friends.size } derivedStateOf
produceState 109
val count by produceState(0) { (1..10).forEach { delay(1000) value =
it } } Text(text = "Count = $count") produceState
val count by produceState(0) { (1..10).forEach { delay(1000) value =
it } } Text(text = "Count = $count") produceState 初期値 0 初期値 0
val count by produceState(0) { (1..10).forEach { delay(1000) value =
it } } Text(text = "Count = $count") produceState 1から10までを1秒ごとにループ
val count by produceState(0) { (1..10).forEach { delay(1000) value =
it } } Text(text = "Count = $count") produceState value に値を設定 1
val count by produceState(0) { (1..10).forEach { delay(1000) value =
it } } Text(text = "Count = $count") produceState value に値を設定 count に設定される 1 2
val count by produceState(0) { (1..10).forEach { delay(1000) value =
it } } Text(text = "Count = $count") produceState value に値を設定 count に設定される 表示 1 2 3
クリーンアップが必要な時 116
val count by produceState(0) { // ... awaitDispose { //
... } } produceState クリーンアップ処理
snapshotFlow 118
snapshotFlow var count by remember { mutableIntStateOf(0) } LaunchedEffect(Unit) {
snapshotFlow { count }.map { "Count = $it" }.collect { Timber.d(it) } }
var count by remember { mutableIntStateOf(0) } LaunchedEffect(Unit) { snapshotFlow
{ count }.map { "Count = $it" }.collect { Timber.d(it) } } snapshotFlow 更新されていくカウント
var count by remember { mutableIntStateOf(0) } LaunchedEffect(Unit) { snapshotFlow
{ count }.map { "Count = $it" }.collect { Timber.d(it) } } snapshotFlow
var count by remember { mutableIntStateOf(0) } LaunchedEffect(Unit) { snapshotFlow
{ count }.map { "Count = $it" }.collect { Timber.d(it) } } snapshotFlow State を Flow に変換
var count by remember { mutableIntStateOf(0) } LaunchedEffect(Unit) { snapshotFlow
{ count }.map { "Count = $it" }.collect { Timber.d(it) } } snapshotFlow Flow の処理
var firstName by remember { mutableStateOf("") } var lastName by
remember { mutableStateOf("") } LaunchedEffect(Unit) { snapshotFlow { "$firstName $lastName" }.collect { Timber.d(it) } } snapshotFlow 2つのStateを組み合わせた結果
まとめ 125
まとめ • LaunchedEffect ◦ 状態が変わった時に何かしたい時 ◦ Coroutines • rememberCoroutineScope ◦
UIイベントからCoroutinesの処理をしたい時 • DisposableEffect ◦ 何かクリーンアップが必要な時 • rememberUpdatedState ◦ 変化する状態を参照している時 126
まとめ • SideEffect ◦ Compositionが成功した時に何かしたい時 ◦ ログ • derivedStateOf ◦
状態から別の値を派生させたい時 • produceState ◦ Compose外の状態をComposeのStateに変換したい時 • snapshotFlow ◦ ComposeのStateの更新をCompose外でFlowで受け取りたい時 127
ありがとうございました 128