Hey Guys, I have a question regarding the recompos...
# compose
b
Hey Guys, I have a question regarding the recomposition.
Copy code
val list: List<SomeData> by somethingFlow.collectAsState() // somethingFlow is StateFlow<List<SomeData>>
LazyColum {
	items(list, key = { it.id }) {
		SomeDataDetail(…) {
			vm.updateBookMark(true)
		}
	}
}
So when
vm.updateBookMark
is done
somethingFlow
gets new data (It is StateFlow as mentioned) . If
SomeData.isBookMark
is changed in any item this will cause the StateFlow to return emit the new List which will cause the entire visible items to recompose. I have checked the nowInAndroid app, Visible Items List are recomposed Question: Is recomposing all the visible items is the best practise? If not what are the alternatives? My case is small items in the List like todoList enabled item or not.(I really don't want the other items in the list to recompose just because the partiular item's state is changed)
v
If your
SomeDataDetail
is skippable, only the list items whose parameters have changed will be recomposed. See https://developer.android.com/jetpack/compose/lifecycle#skipping
If a composable is already in the Composition, it can skip recomposition if all the inputs are stable and haven't changed.
b
Hi @vide That is also what I thought and also checked the compose compiler reports. I see that particular composable is marked as
restartable skippable scheme
So it should have skipped but It isn't.
v
If it's marked as skippable and it isn't, have you checked that the inputs for unrelated items have not also changed accidentally?
b
Let me take another look on it.
I think I get what exactly going on but let me describe case I tested. • When I check the previous and new list’s equality it return true. • I updated the StateFlow it triggers a recomposition to the composable that collects this flow. • Since it is a List<Something> which is inherently unstable so it recomposes the composable that uses List<Something> • In my case, it is a LayColumn so it pass the new List to its items and visible items will be composable at the place. The composable inside the items will use the SomeData since composer knows the previous instance it performance equality checks and skips those Am I understanding this right @vide?
v
Yes, that looks to be correct to me. Is this what you were looking for?
You can also use immutable collections: https://github.com/Kotlin/kotlinx.collections.immutable for immutable lists that allow skipping
b
I looking at whenever new List is received
Copy code
inline fun <T> LazyListScope.items(
    items: List<T>,
    noinline key: ((item: T) -> Any)? = null,
    noinline contentType: (item: T) -> Any? = { null },
    crossinline itemContent: @Composable LazyItemScope.(item: T) -> Unit
)
I was expecting itemContent to be never called for T that did not change.
v
(Providing a key is also a good idea if the order of items in the list can change)
@Bagadeshkumar R I think the confusion is coming from the fact that compose doesn't generate recomposition scopes for inline functions. Even though the lambda is composable, it doesn't get its own recomposition scope, but uses the parent's recomposition scope instead. In this case both
items
and
itemContent
are inlined to the parent,
LazyColumn
b
Get it. So all visible items in the items will be recomposed we just have make sure the composable we put inside the itemContent are skippable
v
Check out https://kotlinlang.org/docs/inline-functions.html for inline behaviour in Kotlin (lambdas passed to an inline function are also inlined by default) and https://dev.to/zachklipp/scoped-recomposition-jetpack-compose-what-happens-when-state-changes-l78
For every non-inline composable function that returns
Unit
, the Compose compiler generates code that wraps the function’s body in a recompose scope. When a recompose scope is invalidated, the compose runtime will ensure the (entire) function body gets recomposed (reexecuted) before the next frame. Functions are a natural delimiter for re-executable chunks of code, because they already have well-defined entry and exit points.
`Foo`’s body, `Button`’s body, the content lambda we pass to
Button
, `Text`’s body, all get their own recompose scopes.
What about inline functions and functions that return a value?
Inline functions are inline - their bodies are effectively copied into their caller, so they share their caller’s recompose scope.
Functions that have a non-unit return value don’t get their own scopes because they can’t be re-executed without also re-executing their caller, so their caller can “see” the new return value computed by the function.
b
Thanks @vide for helping out to figure this out.
v
No problem! I remember being quite confused myself at the start, and still am about some deeper topics