英文:
Why this code is causing recomposition (jetpack compose)
问题
我不知道为什么在我的代码中,我输入“hi”的文本总是被重新组合。
文本已固定为“hi”,并且 onClick 也使用 uiEvent remember 来保持实例。
即使 uiState.isLoading 被更改,它与 Text 无关,所以我预期会跳过重新组合,但是当 Text 更改时,重新组合总是发生。
这是我的代码:
UiState 数据类
data class UiState(
val isLoading: Boolean
)
UiEvent 接口
@Immutable
interface UiEvent {
fun onClick()
}
可组合函数
@Composable
fun MyScreen(
viewModel: MyViewModel = hiltViewModels()
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
val uiEvent = remember {
object: UiEvent {
override fun onClick() {
viewModel.action()
}
}
}
Text( // 为什么重新组合?
modifier = Modifier.clickable(onClick = uiEvent::onClick),
text = "hi"
)
if (uiState.isLoading) {
Text(text = "isLoading")
}
}
英文:
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:
UiState Data Class
data class UiState(
val isLoading: Boolean
)
UiEvent Interface.
@Immutable
interface UiEvent {
fun onClick()
}
Composable
@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")
}
}
答案1
得分: 7
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 limitations by remembering the entire modifier.
英文:
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.
答案2
得分: 1
我已经制作了一个简单的布局,包含3个文本:
- 点击时的静态文本
- 静态文本
- 动态文本(带有点击计数)
这是在3次点击后重新组合计数的样子:
可点击的文本在每次点击后重新组合。已在Compose BOM 2023.08.00上进行了测试。
解决方法是将可点击的修饰符包装在remember
中,但我不确定是否应该将其作为使用可点击修饰符的默认方式。
英文:
I have made a simple layout with 3 texts:
- Static text with on click
- Static text
- Dynamic text (with clicks count)
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论