Reputation: 551
I don't know why in my code the text where I type "hi" is always recomposed. The text was fixed to "hi", and onClick also maintained the instance using uiEvent remember.
Even if uiState.isLoading is changed, it is not related to Text, so I expected that recomposition would be skipped, but recomposition always occurs when Text is changed.
Here is my code:
data class UiState(
val isLoading: Boolean
)
@Immutable
interface UiEvent {
fun onClick()
}
@Composable
fun MyScreen(
viewModel: MyViewModel = hiltViewModels()
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
val uiEvent = remember {
object: UiEvent {
override onClick() {
viewModel.action()
}
}
}
Text( // why recomposition?
modifier = Modifier.clickable(onClick = uiEvent::onClick),
text = "hi"
)
if (uiState.isLoading) {
Text(text = "isLoading")
}
}
Upvotes: 8
Views: 3502
Reputation: 9
I have made a simple layout with 3 texts:
This is how recompositions count looks after 3 clicks:
Clickable Text is recomposed every click. Tested on Compose BOM 2023.08.00
Workaround is to wrap clickable modifier with remember
but I am not sure if it should be default way to use clickable Modifier.
Upvotes: 0
Reputation: 29575
I assume what you are asking is why does Text
not skip whenever MyScreen
recomposes. The reason is a new of the clickable modifier is being created every time. Consider remembering the entire modifier such as:
@Composable
fun MyScreen(
viewModel: MyViewModel = hiltViewModels()
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
val modifier = remember(viewModel) {
val event = object: UiEvent {
override fun onClick() {
viewModel.action()
}
}
Modifier.clickable { event.onClick() }
}
Text( // why recomposition?
modifier = modifier,
text = "hi"
)
if (uiState.isLoading) {
Text(text = "isLoading")
}
}
Note the addition of the viewModel
as a parameter to the remember to ensure a new version of clickable()
gets created if/when the viewModel
changes.
The compose compiler plugin should recognize the ::
syntax and do the remember
for you, like it does for lambda syntax, but it doesn't right now. Also, the clickable()
always returns a modifier that is not equal to the previous version for the same parameters, but that is going to be fixed in 1.5 (along with quite a number of other performance related fixes for clickable()
). For now, you can work around these limitation by remembering the entire modifier.
Upvotes: 9