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
600
Android 14 と Predictive back gesture / Shibuya.apk #42
star_zero
0
390
Coroutines Test 入門 / Android Test Night #8
star_zero
2
1.1k
What's new in Jetpack / I/O Extended Japan 2022
star_zero
1
640
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
710
Other Decks in Programming
See All in Programming
来たるべき 8.0 に備えて React 19 新機能と React Router 固有機能の取捨選択とすり合わせを考える
oukayuka
2
770
[初登壇@jAZUG]アプリ開発者が気になるGoogleCloud/Azure+wasm/wasi
asaringo
0
130
実践ArchUnit ~実例による検証パターンの紹介~
ogiwarat
2
260
ドメインモデリングにおける抽象の役割、tagless-finalによるDSL構築、そして型安全な最適化
knih
11
1.9k
Elixir で IoT 開発、 Nerves なら簡単にできる!?
pojiro
1
140
Development of an App for Intuitive AI Learning - Blockly Summit 2025
teba_eleven
0
120
統一感のある Go コードを生成 AI の力で手にいれる
otakakot
0
3k
複数アプリケーションを育てていくための共通化戦略
irof
10
3.9k
Javaに鉄道指向プログラミング (Railway Oriented Pro gramming) のエッセンスを取り入れる/Bringing the Essence of Railway-Oriented Programming to Java
cocet33000
2
580
UPDATEがシステムを複雑にする? イミュータブルデータモデルのすすめ
shimomura
1
550
関数型まつりレポート for JuliaTokai #22
antimon2
0
120
Using AI Tools Around Software Development
inouehi
0
1.2k
Featured
See All Featured
It's Worth the Effort
3n
184
28k
Building Adaptive Systems
keathley
43
2.6k
Agile that works and the tools we love
rasmusluckow
329
21k
Embracing the Ebb and Flow
colly
86
4.7k
Building Flexible Design Systems
yeseniaperezcruz
328
39k
Bootstrapping a Software Product
garrettdimon
PRO
307
110k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.7k
Fireside Chat
paigeccino
37
3.5k
RailsConf 2023
tenderlove
30
1.1k
The Invisible Side of Design
smashingmag
299
51k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
[RailsConf 2023] Rails as a piece of cake
palkan
55
5.6k
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