Compose 기초 - Composable의 수명주기, Smart Recomposition
Composable의 수명주기 (원문)
💡개념이 헷갈리거나 제가 잘 모르거나 많은 사람들이 잘 모를 것 같은 것 위주로 정리
Composable의 수명 주기
Initial Composition 이후 0회 이상 Recomposition 되고 Composition이 종료된다.
Smart Recomposition에 도움이 되는 정보 추가하기
목록 중간에 항목을 추가/삭제하는 경우, 목록이 재정렬되는 경우 모든 항목은 Recomposition의 대상이 된다.
Column
또는 LazyColumn
Composable 아래에서 key
Composable을 사용하면 변동이 없는 항목을 Recomposition 대상에서 제외시킬 수 있다.
@Composable
fun Screen() {
var items by remember {
mutableStateOf((0 until 1000).toList())
}
val scope = rememberCoroutineScope()
ItemList(
items = items,
onItemClick = {
// scope 안에서 데이터를 변경하면 key가 제 역할을 하지 못한다. ViewModel에서 데이터를 변경해도 마찬가지일 것. rememberUpdatedState()의 개념이 힌트일지도 모르겠다.
// LazyColumn에선 key가 제대로 역할을 못하더라도 LazyColumn이 화면에 보여지는 항목들만 Recomposition 대상으로 삼기 때문에 성능문제가 크진 않다.
// scope.launch {
val newItems = items.takeLast(items.size - 1)
items = newItems
// }
},
)
}
@Composable
private fun ItemList(items: List<Int>, onItemClick: () -> Unit) {
LazyColumn(Modifier.fillMaxSize()) {
items(
items = items,
key = { item -> item },
) { item ->
// 이 위치에선 아래와 같이 `@Composable` 함수를 호출하지 않고 Box { ... } 처럼 직접 풀어쓰면 key가 제대로 역할을 하지 못한다. (원인 모름)
// 이 위치에선 아래와 같이 `@Composable` 함수를 호출하는 것 외에 실행코드 한 줄이라도 있으면 key가 제대로 역할을 하지 못한다. 예) Log.e("BSSCCO", "$item"). (원인 모름)
Item(item, onClick = onItemClick)
}
}
}
@Composable
private fun Item(item: Int, onClick: () -> Unit) {
Box(
modifier = Modifier
.clickable(onClick = onClick)
.fillMaxWidth()
.height(50.dp),
contentAlignment = Alignment.Center,
) {
Log.e("BSSCCO", "$item")
Text(item.toString())
}
}
key
사용 시 주의할 점
coroutineScope 안에서 데이터를 변경하면 key
가 제대로 역할을 하지 못한다. ViewModel에서 데이터를 변경해도 마찬가지일 것. rememberUpdatedState()
의 지연된 상태접근 개념이 원인의 힌트가 될지도 모르겠다..
items
안에선 Composable
함수를 호출하지 않고 Box { ... }
처럼 직접 풀어쓰면 key
가 제대로 역할을 하지 못한다. (원인 모름)
items
안에선 Composable
함수를 호출하는 것 외에 실행코드 한 줄이라도 있으면 key
가 제대로 역할을 하지 못한다. 예) Log.e("BSSCCO", "$item")
. (원인 모름)
LazyColumn
에선 key
가 제대로 역할을 못하더라도 LazyColumn
은 화면에 보여지는 항목들만 Recomposition 대상으로 삼기 때문에 성능문제가 크진 않다.
Recomposition에서 제외되는 조건
모든 입력(@Composable
함수의 매개변수, 함수 안에서 사용하고 있는 State<T>
변수 등)이 안정적이고 변경되지 않았으면 건너띈다.
안정적인 자료형
- 모든 원시 값 유형: Boolean, Int, Long, Float, Char 등
- 문자열
- 모든 함수 유형(람다)
- 완전히 변경 불가능한 모든 유형
변경되지 않았는지 비교하는 방법
두 인스턴스의 equals 결과가 동일한지 비교한다.
안정적인 것으로 강제하기
Compose가 유형이 안정적이라고 추론할 수 없지만 안정적인 것으로 간주하도록 하려면 @Stable 어노테이션을 사용한다.
@Stable
interface UiState<T : Result<T>> {
val value: T?
val exception: Throwable?
val hasError: Boolean
get() = exception != null
}