Optimize list recomposition in derivedStateOf

In Jetpack Compose, we often use derivedStateOf to calculate a derived state based on other states. However, we may misuse it and cause unnecessary recomposition in some cases. In this article, I will show you how to optimize list recomposition in derivedStateOf.

The problem

Here is an actual example that exist in my wwm project.

1
2
3
4
5
6
7
8
9
val achievementGroupId by rememberUpdatedState(achievementGroup.id) // Int
val markedAchievementIds by rememberUpdatedState(state.markedAchievementIds) // List<Int>
val achievements by remember(achievementGroupId, markedAchievementIds) {
derivedStateOf {
state.achievements
.filter { achievement -> achievement.groupId == achievementGroupId }
.sortedBy { achievement -> achievement.id in markedAchievementIds }
}
}

In this code snippet, we use rememberUpdatedState(state.markedAchievementIds) to observe the markedAchievementIds list. When the markedAchievementIds list changes. However, the object of markedAchievementIds will always be different, even if the content is the same. This will cause the achievements state to recompose two times.

The solution

We should use SnapshotStateList to store and update the markedAchievementIds list. This way, we can ensure that the object of markedAchievementIds will not be changed when the content is the same. Thus, we can avoid unnecessary recomposition.

1
2
3
4
5
6
7
8
9
10
11
12
13
val achievementGroupId by rememberUpdatedState(achievementGroup.id)
val markedAchievementIds = remember { mutableStateListOf<Int>() }
.apply {
clear()
addAll(state.markedAchievementIds)
}
val achievements by remember(achievementGroupId, markedAchievementIds) {
derivedStateOf {
state.achievements
.filter { achievement -> achievement.groupId == achievementGroupId }
.sortedBy { achievement -> achievement.id in markedAchievementIds }
}
}