derivedStateOf in Jetpack Compose
derivedStateOf in Jetpack Compose
Jetpack Compose provides derivedStateOf as a state management utility that creates a state object whose value is computed from other observable states. The key benefit is that the derived computation only re-executes when its input states actually change, not on every recomposition. This prevents redundant work and avoids unnecessary recompositions in performance sensitive scenarios. By the end of this lesson, you will be able to:
- Explain how
derivedStateOftracks dependencies and avoids redundant computation. - Distinguish when
derivedStateOfis the right tool versusremember(key). - Apply
derivedStateOfcorrectly withrememberto prevent recreation on recomposition. - Identify common misuses that lead to stale state or wasted computation.
How derivedStateOf Works Internally
derivedStateOf creates a DerivedSnapshotState object. When Compose reads the value for the first time, it evaluates the lambda and records every State object that was read during execution. On subsequent reads, Compose checks whether any of those recorded dependencies have changed. If none have changed, it returns the cached result without re-executing the lambda.
This dependency tracking happens through the snapshot system. Each State read inside the lambda is registered as a dependency of the derived state. When any dependency changes, the derived state is marked as potentially stale. The next read triggers re-evaluation:
@Composable
fun FilteredList(items: List<String>, query: String) {
val filteredItems by remember(items, query) {
derivedStateOf {
items.filter { it.contains(query, ignoreCase = true) }
}
}
LazyColumn {
items(filteredItems) { item ->
Text(text = item)
}
}
}
In this example, the filtering lambda runs only when items or query changes. Other state changes in the composable do not trigger the filter computation again.
derivedStateOf vs remember with Keys
A common question is when to use derivedStateOf versus remember(key1, key2) { computation() }. The distinction comes down to what triggers re-evaluation.
remember(key) re-executes its lambda whenever the key changes, and the key is compared by equality on each recomposition. derivedStateOf re-executes only when the State objects read inside the lambda change. This matters when the source of truth is a State object that changes frequently but the derived value changes rarely:
@Composable
fun ScrollIndicator(listState: LazyListState) {
// Good: derivedStateOf filters out scroll positions
// where the button visibility does not change
val showButton by remember {
derivedStateOf {
listState.firstVisibleItemIndex > 0
}
}
if (showButton) {
ScrollToTopButton()
}
}
This interview continues for subscribers
Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.
Become a Sponsor