Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Jetpack Compose の Side-effect を使いこなす / DroidKai...
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
star_zero
September 15, 2023
Programming
7.3k
5
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Jetpack Compose の Side-effect を使いこなす / DroidKaigi 2023
star_zero
September 15, 2023
More Decks by star_zero
See All by star_zero
今からはじめるAndroidアプリ開発 2024 / DevFest 2024
star_zero
0
1.7k
Android 14 新機能 / Android 14 Meetup Nagoya
star_zero
1
700
Android 14 と Predictive back gesture / Shibuya.apk #42
star_zero
0
520
Coroutines Test 入門 / Android Test Night #8
star_zero
2
1.4k
What's new in Jetpack / I/O Extended Japan 2022
star_zero
1
730
Kotlin 2021 Recap / DevFest 2021
star_zero
3
1.4k
Kotlin Symbol Processing (KSP) を使ったコード生成 / DroidKaigi 2021
star_zero
2
5.3k
What's new Android 12
star_zero
0
640
これからはじめるAndroid開発 / DevFest 2020
star_zero
4
790
Other Decks in Programming
See All in Programming
mrbgem 三角測量 開発
ogom
0
240
コードレビューのボトルネックを"する側"と"される側"の両面から解消する
yub0n
2
1.6k
ActionController meets Protocol Buffers
sorah
0
240
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
480
最近”作らせてる” ローカルAIアプリの話
akit37
0
460
コーディング自動化の次へ。人の介入をゼロにするファクトリーとサポートAIによる実践
timakin
2
1.8k
Simple Storage Service(S3) is not simple
iwatsukayura
0
280
現場と育てるRailsモデリング 〜Spreadsheetから始めたモデル設計〜
siroemk
2
320
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
470
Railsバージョンを4倍にした話
optim
0
110
パーソナライズPodcastを作った話
repunit11
0
140
Rでドレミ/Do-Re-Mi_with_R
florets1
0
130
Featured
See All Featured
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
520
The SEO Collaboration Effect
kristinabergwall1
1
590
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
310
Heart Work Chapter 1 - Part 1
lfama
PRO
10
37k
Building an army of robots
kneath
307
47k
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
620
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
700
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
590
How to build a perfect <img>
jonoalderson
1
6.1k
Building a Scalable Design System with Sketch
lauravandoore
464
34k
Mobile First: as difficult as doing things right
swwweet
225
10k
Practical Orchestrator
shlominoach
192
12k
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