728x90
이전 포스팅에서 이어서 작성.
https://jjunsu.tistory.com/383
derivedStateOf: 하나 이상의 상태 객체를 다른 상태로 변환
특정 상태가 계산되거나 다른 상태 개체에서 파생되는 경우 derivedStateOf를 사용한다.
derivedStateOf를 사용하면 계산에 사용된 상태 중 하나가 변경될 때마다 계산이 실행된다.
@Composable
fun TodoList(highPriorityKeywords: List<String> = listOf("Review", "Unblock", "Compose")) {
val todoTasks = remember { mutableStateListOf<String>() }
// Calculate high priority tasks only when the todoTasks or highPriorityKeywords
// change, not on every recomposition
val highPriorityTasks by remember(highPriorityKeywords) {
derivedStateOf { todoTasks.filter { it.containsWord(highPriorityKeywords) } }
}
Box(Modifier.fillMaxSize()) {
LazyColumn {
items(highPriorityTasks) { /* ... */ }
items(todoTasks) { /* ... */ }
}
/* Rest of the UI where users can add elements to the list */
}
}
- 위 코드에서 DerivateStateOf는 todoTasks가 변경될 때마다 highPriorityTasks 계산이 실행되고, 이에 따라 UI가 업데이트되도록 보장한다.
- highPriorityTasks가 변경되면 remember 블록이 실행되고 새로운 파생 상태 개체가 생성되어 이전 상태 개체 대신 기억된다.
- highPriorityTasks를 계산하기 위한 필터링은 비용이 많이 들 수 있으므로 재구성할 때마다가 아닌 목록이 변경될 때만 실행되어야 한다.
- derivedStateOf에 의해 상태가 업데이트되어도 업데이트가 선언된 Composable이 재구성되지 않는다. Composable는 예의 LazyColumn 내에서 반환된 상태를 읽는 위치의 Composable만 재구성한다.
snapshotFlow: Compose의 상태를 Flow로 변환
snapshotFlow를 사용하여 State<T> 객체를 Cold Flow로 변환한다.
- Cold Flow: 각각의 소비자가 수집을 시작하면 데이터를 발행(각 호출마다 실행되므로 서로 다른 Flow 객체로부터 수집), 스트림 생성 후 구독하지 않으면 어떤 연산도 발생하지 않는다.
snapshotFlow는 수집될 때 블록을 실행하고 읽은 State 개체의 결과를 내보낸다.
snapshotFlow 블록 내부에서 읽은 State 객체 중 하나가 변경되면 새 값이 이전에 내보낸 갑과 같지 않으면 Flow는 새 값을 수집기로 내보낸다.
val listState = rememberLazyListState()
LazyColumn(state = listState) {
// ...
}
LaunchedEffect(listState) {
snapshotFlow { listState.firstVisibleItemIndex }
.map { index -> index > 0 }
.distinctUntilChanged()
.filter { it == true }
.collect {
MyAnalyticsService.sendScrolledPastFirstItemEvent()
}
}
- listState.firstVisibleItemIndex는 Flow 연산자의 이점을 활용할 수 있는 Flow로 변환된다.
Restarting effects: 효과 다시 시작
LaunchedEffect, produceState, DisposableEffect와 같은 Compose의 일부 효과는 실행 중인 효과를 취소하고 새 키로 새 효과를 시작하는데 사용되는 가변 개수의 인수 키를 사용한다.
EffectName(restartIfThisKeyChanges, orThisKey, orThisKey, ...) { block }
이 동작은 미묘하므로 효과를 다시 시작하는 데 사용된 매개변수가 올바르지 않은 경우 문제가 발생할 수 있다.
- 필요한 것보다 적은 효과를 다시 시작하면 앱에 버그가 발생할 수 있다.
- 필요한 것보다 더 많은 효과를 다시 시작하면 비효율적일 수 있다.
@Composable
fun HomeScreen(
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
onStart: () -> Unit, // Send the 'started' analytics event
onStop: () -> Unit // Send the 'stopped' analytics event
) {
// These values never change in Composition
val currentOnStart by rememberUpdatedState(onStart)
val currentOnStop by rememberUpdatedState(onStop)
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
/* ... */
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
- currentOnStart 및 currentOnStop은 rememberUpdatedState 사용으로 인해 Composition에서 해당 값이 변경되지 않기 때문에 DisposableEffect 키로 필요하지 않다.
- lifecycleOwner를 매개변수로 전달하지 않고 변경하면 HomeScreen이 재구성되지만 DisposableEffect가 삭제되거나 다시 시작되지 않는다.
728x90
'Android > Compose' 카테고리의 다른 글
[Compose] LazyColumn이란? (0) | 2023.07.11 |
---|---|
[Compose] @Preview 분석 (0) | 2023.06.27 |
[Compose] Compose의 Side-Effect(2) (0) | 2023.05.09 |
[Compose] Compose의 Side-Effect(1) (0) | 2023.04.25 |
[Compose] Compose에서의 상태 (0) | 2023.04.03 |
댓글