Jerry Johns
10/18/2024, 3:44 AMvar selectedItemSnippet: String? by remember { mutableStateOf(null) }
val itemsList: List<String> = listOf("a", "b", "c")
val selectedItem = remember(selectedItemSnippet) { itemsList.firstOrNull { it == selectedItemSnippet } }
LazyColumn(
modifier = Modifier.padding(paddingValues),
contentPadding = PaddingValues(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
items(
items = itemsList,
key = { item -> item }
) {
Card(
modifier = Modifier.clickable {
selectedItemSnippet = it
}
) {
Text("Hello ${it}")
}
}
}
If I remove the val selectedItem = rememb...
line, then no recomposition happens.
At a loss for words as to what's happening here. Any help is greatly appreciated!Zach Klippenstein (he/him) [MOD]
10/18/2024, 3:49 AMJerry Johns
10/18/2024, 3:52 AMJerry Johns
10/18/2024, 3:56 AMJerry Johns
10/18/2024, 3:56 AMYou’re allocating a new list instance each timeWhere exactly am I doing so, and what's the implication?
Zach Klippenstein (he/him) [MOD]
10/18/2024, 3:59 AMselectedItemSnippet
in the outer computable when you pass it as the key to remember for selectedItem
. This causes the outer composable to recompose every time you select a new item. When this happens, you allocate a new items
list since it’s not remembered. This means that the LazyColumn
sees it as a completely new list and probably what causes every item to be recomposed. This might not happen if strong skipping is enabled, I’m not sure. OrZach Klippenstein (he/him) [MOD]
10/18/2024, 4:00 AMselectedItem
eliminates the recompositions because it removes the state read.Jerry Johns
10/18/2024, 4:03 AMval itemsList: List<String> = listOf("a", "b", "c")
to:
val itemsList: List<String> = remember { listOf("a", "b", "c") }
Still no dice, I still see full recomposition.Zach Klippenstein (he/him) [MOD]
10/18/2024, 4:06 AMLazyColumn
has always recomposed its contents more aggressively than it seems like it needs to, so it might just do it every time it’s called. You could make selectedItem
a derived state.Ben Trengrove [G]
10/18/2024, 4:15 AMclickable
to the following to get the non-composed version
clickable(
interactionSource = null,
indication = ripple(),
onClick = { selectedItemSnippet = it }
)
Jerry Johns
10/18/2024, 4:24 AMvar selectedItemSnippet: String? by remember { mutableStateOf(null) }
val itemsList: List<String> = remember { listOf("a", "b", "c") }
LazyColumn(
modifier = Modifier.padding(paddingValues),
contentPadding = PaddingValues(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
items(
items = itemsList,
key = { item -> item }
) {
val wasSelected by remember { derivedStateOf { it == selectedItemSnippet } }
Card(
modifier = Modifier.clickable(
interactionSource = null,
indication = ripple()
) {
selectedItemSnippet = it
}
) {
Log.e("Test", "Recomposing ${it}")
Text("Hello ${it} ${wasSelected}")
}
}
}
Ben Trengrove [G]
10/18/2024, 4:27 AMval wasSelected = it == selectedItemSnippet
Ben Trengrove [G]
10/18/2024, 4:29 AMkey = { item -> item }
with that you can't have duplicate strings in your list. You can just delete it and it will use the index of the itemJerry Johns
10/18/2024, 4:29 AMJerry Johns
10/18/2024, 4:30 AMBen Trengrove [G]
10/18/2024, 4:35 AMBen Trengrove [G]
10/18/2024, 4:38 AMvar selectedItemSnippet: String? by remember { mutableStateOf(null) }
val itemsList: List<String> = remember { listOf("a", "b", "c", "d", "e") }
LazyColumn(
contentPadding = PaddingValues(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
items(items = itemsList) {
MyItem(
item = it,
selected = it == selectedItemSnippet,
onClick = {
selectedItemSnippet = it
}
)
}
}
@Composable
fun MyItem(
item: String,
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier.clickable(onClick = onClick)
) {
Log.e("Test", "Recomposing ${item}")
Text("Hello ${item} ${selected}")
}
}
Ben Trengrove [G]
10/18/2024, 4:39 AMJerry Johns
10/18/2024, 4:43 AMJerry Johns
10/18/2024, 4:51 AM@Composable
fun MyItem(
item: String,
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val item2 by remember{ derivedStateOf { item } }
Card(
modifier = modifier.clickable(onClick = onClick)
) {
Log.e("Test", "Recomposing ${item2}")
Text("Hello ${item2} ${selected}")
}
Card(
modifier = modifier.clickable(onClick = onClick)
) {
Log.e("Test", "Recomposing2 ${item2}")
Text("Hello2 ${item2}")
}
}
Anytime I click on one of the items (after clicking on another previously), I see this:
10-17 21:49:46.728 32259 32259 E Test : Recomposing a
10-17 21:49:46.729 32259 32259 E Test : Recomposing2 a
10-17 21:49:46.729 32259 32259 E Test : Recomposing b
10-17 21:49:46.729 32259 32259 E Test : Recomposing2 b
I would have thought that the second Card instance wouldn't have recomposed? (which doesn't depend on selected, and only depends on item
, which isn't changing)Ben Trengrove [G]
10/18/2024, 4:54 AMBen Trengrove [G]
10/18/2024, 4:55 AM.clickable(onClick = onClick)
this makes composables unskippable. My snippet works around that by moving it into it's own scope so it doesn't matterJerry Johns
10/18/2024, 4:58 AM@Composable
fun MyItem(
item: String,
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val item2 by remember{ derivedStateOf { item } }
val onClick2 = remember(onClick) { onClick }
Card {
Log.e("Jerry", "Recomposing2 ${item2}")
Text("Hello2 ${item2}")
}
Card(
modifier = modifier.clickable(
interactionSource = null,
indication = ripple(),
onClick = onClick2
)
) {
Log.e("Jerry", "Recomposing ${item2}")
Text("Hello ${item2} ${selected}")
}
}
Ben Trengrove [G]
10/18/2024, 4:59 AMJerry Johns
10/18/2024, 4:59 AMJerry Johns
10/18/2024, 4:59 AMBen Trengrove [G]
10/18/2024, 5:00 AMBen Trengrove [G]
10/18/2024, 5:00 AMitem2
, that invalidates that scopeJerry Johns
10/18/2024, 5:00 AMBen Trengrove [G]
10/18/2024, 5:11 AMBen Trengrove [G]
10/18/2024, 5:13 AMJerry Johns
10/18/2024, 5:17 AM