11월 2주차에는 derivedStateOf 의 활용에 대한 글입니다.
https://saurabharora.dev/posts/navigating-pitfalls-when-to-use-derivedStateOf-with-keys/
소개
이 글은 Jetpack Compose에서 derivedStateOf를 효과적으로 활용하는 방법과 그 사용시 발생할 수 있는 문제들을 해결하는 방법에 대한 글입니다. 해시태그 유효성 검사 예제를 통해 derivedStateOf의 사용과 잠재적인 문제점들을 설명해줍니다. 평소 상태관리할때 아무생각없이 remember쓰고 mutableStateOf 썼는데 조금더 자세한 과정을 알게된 글입니다.
이분 글이 정말 잘 정리되어있습니다. 기사보다도 잘 정리된글!
내용
1. derivedStateOf의 개념 및 해시태그 유효성 검사 예제
- derivedStateOf는 상태나 키가 자주 변경되지만 특정 조건에서만 UI 재구성이 필요할 때 사용합니다.
- 해시태그 유효성 검사 예제에서는 사용자가 해시태그를 입력하고, 유효하지 않은 경우 오류 메시지를 표시합니다.
@Composable
private fun PostHashtags(
hashtags: ImmutableList<String>,
onAddHashTag: (String) -> Unit,
modifier: Modifier
) {
var inputHashTag by remember { mutableStateOf("") }
val isValidHashtag by remember(inputHashTag) {
derivedStateOf { !inputHashTag.contains(" ") }
}
Column(modifier = modifier) {
// OutlinedTextField와 다른 UI 요소들
}
}
2. 예상치 못한 문제 및 로그 추가
- 첫 해시태그 추가 후 오류 메시지가 표시되지 않는 문제 발생.
- 로그를 추가하여 derivedStateOf 블록이 호출되지 않는 것을 확인.
val isValidHashtag by remember(inputHashTag) {
derivedStateOf {
Log.d("PostHashtags", "Calculating isValidHashtag")
!inputHashTag.contains(" ")
}
}
3. 문제의 근본 원인 및 해결 방법
- remember 함수는 hashtags 목록이 변경될 때마다 inputHashTag의 새로운 값을 생성하고 기억합니다. 그러나 derivedStateOf는 이러한 변경을 감지하지 못합니다.
- 해결책으로 remember를 inputHashTag 상태 객체로 키 설정하여, 새로운 상태 객체가 생성될 때마다 derivedStateOf가 이를 감지하도록 합니다.
val inputHashTag = remember(hashtags) { mutableStateOf("") }
val isValidHashtag by remember(inputHashTag) {
derivedStateOf { !inputHashTag.value.contains(" ") }
}
4. 글에서의 총 정리 코드
@Composable
private fun PostHashtags(
hashtags: ImmutableList<String>,
onAddHashTag: (String) -> Unit,
modifier: Modifier
) {
- var inputHashTag by remember(hashtags) { mutableStateOf("") }
+ val inputHashTag = remember(hashtags) { mutableStateOf("") }
- val isValidHashtag: Boolean by remember {
+ val isValidHashtag: Boolean by remember(inputHashTag) {
derivedStateOf {
- !inputHashTag.contains(" ")
+ !inputHashTag.value.contains(" ")
}
}
Column(modifier = modifier) {
OutlinedTextField(
- value = inputHashTag,
+ value = inputHashTag.value,
- onValueChange = { inputHashTag = it },
+ onValueChange = { inputHashTag.value = it },
leadingIcon = { Text(text = "#", fontWeight = FontWeight.Bold) },
placeholder = { Text(text = "Type a hashtag here") },
trailingIcon = {
AnimatedVisibility(
- visible = inputHashTag.isNotEmpty() && isValidHashtag,
+ visible = inputHashTag.value.isNotEmpty() && isValidHashtag,
enter = fadeIn(),
exit = fadeOut()
) {
- IconButton(onClick = { onAddHashTag(inputHashTag) }) {
+ IconButton(onClick = { onAddHashTag(inputHashTag.value) }) {
Icon(imageVector = Icons.Default.AddCircle,
contentDescription = "Add hashtag")
}
}
},
isError = !isValidHashtag,
)
AnimatedVisibility(visible = !isValidHashtag) {
Text(
text = "Hashtag cannot have a space",
style = MaterialTheme.typography.labelSmall,
modifier = Modifier.padding(vertical = 2.dp)
)
}
ReusableVerticalLazyList(content = hashtags)
}
}
정리
derivedStateOf는 불필요한 재구성을 줄이는 데 매우 효과적인 도구지만 사용시 주의가 필요합니다. UI 업데이트의 시기와 방법을 제어하여 부드럽고 효율적인 사용자 경험을 보장할 수 있습니다. 신중하게 적용될 때, 더욱 성능이 좋고 반응이 빠른 애플리케이션을 만들 수 있습니다. 따라서 동적 UI 컴포넌트를 다룰 때 재구성을 최적화하고자 한다면 Jetpack Compose에서 derivedStateOf를 고려해보는 것이 좋습니다.
'Kotlin Weekly' 카테고리의 다른 글
Kotlin Weekly # -382 " 호환성 종류와 이해" (0) | 2023.12.12 |
---|---|
Kotlin Weekly # -381 "Kotlin Multiplatform(KMP)의 2024년 로드맵" (0) | 2023.12.12 |
Kotlin Weekly # -379 "Kotlin 1.9.20 의 출시와 변경사항" (0) | 2023.12.12 |
Kotlin Weekly # -378 "Compose Canvas API로 계기판 만들기" (0) | 2023.12.12 |
Kotlin Weekly # -377 "Kotlin 제네릭 변성" (0) | 2023.12.11 |