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
6.1k
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
1.3k
Android 14 新機能 / Android 14 Meetup Nagoya
star_zero
1
610
Android 14 と Predictive back gesture / Shibuya.apk #42
star_zero
0
400
Coroutines Test 入門 / Android Test Night #8
star_zero
2
1.2k
What's new in Jetpack / I/O Extended Japan 2022
star_zero
1
650
Kotlin 2021 Recap / DevFest 2021
star_zero
3
1.3k
Kotlin Symbol Processing (KSP) を使ったコード生成 / DroidKaigi 2021
star_zero
2
5.2k
What's new Android 12
star_zero
0
570
これからはじめるAndroid開発 / DevFest 2020
star_zero
4
720
Other Decks in Programming
See All in Programming
Azure AI Foundryではじめてのマルチエージェントワークフロー
seosoft
0
170
NPOでのDevinの活用
codeforeveryone
0
840
明示と暗黙 ー PHPとGoの インターフェイスの違いを知る
shimabox
2
510
Rails Frontend Evolution: It Was a Setup All Along
skryukov
0
140
RailsGirls IZUMO スポンサーLT
16bitidol
0
190
脱Riverpod?fqueryで考える、TanStack Queryライクなアーキテクチャの可能性
ostk0069
0
140
Startups on Rails in Past, Present and Future–Irina Nazarova, RailsConf 2025
irinanazarova
0
110
Modern Angular with Signals and Signal Store:New Rules for Your Architecture @enterJS Advanced Angular Day 2025
manfredsteyer
PRO
0
220
Porting a visionOS App to Android XR
akkeylab
0
460
AI時代の『改訂新版 良いコード/悪いコードで学ぶ設計入門』 / ai-good-code-bad-code
minodriven
14
4.8k
Google Agent Development Kit でLINE Botを作ってみた
ymd65536
2
250
地方に住むエンジニアの残酷な現実とキャリア論
ichimichi
5
1.5k
Featured
See All Featured
The Cost Of JavaScript in 2023
addyosmani
51
8.5k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
The Language of Interfaces
destraynor
158
25k
Automating Front-end Workflow
addyosmani
1370
200k
Thoughts on Productivity
jonyablonski
69
4.7k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
8
690
Being A Developer After 40
akosma
90
590k
Large-scale JavaScript Application Architecture
addyosmani
512
110k
Rails Girls Zürich Keynote
gr2m
95
14k
Optimizing for Happiness
mojombo
379
70k
[RailsConf 2023] Rails as a piece of cake
palkan
55
5.7k
Adopting Sorbet at Scale
ufuk
77
9.5k
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