为什么使用 LaunchedEffect 并通过按键触发整个 Composable 层次结构的重组?

问题描述 投票:0回答:1

我在 Jetpack Compose 中使用

LaunchedEffect(key)
时偶然发现了一个奇怪的问题,并将其追溯到以下最小示例:

Surface(
    modifier = Modifier.fillMaxSize().safeDrawingPadding(),
    color = MaterialTheme.colorScheme.background
) {

    var pseudoState by remember {
        mutableStateOf(false)
    }

    LaunchedEffect(Unit) {}  // NOTE: I am using Unit here

    Column {
        Button(onClick = { pseudoState = !pseudoState }) {
            Text(text = "TOGGLE pseudoState to ${!pseudoState}")
        }
        Text(text = "Random: ${Math.random()}")
    }
}

当我运行它时,请注意,单击后

Button
已正确重新组合,并且会跳过其他
Text
可组合项。

enter image description here


现在,我做了一个小调整,将

pseudoState
作为
key
提供给空的
LaunchedEffect

LaunchedEffect(pseudoState) {}  // NOTE: I am using pseudoState now

现在,每单击一次

Button
Button
Text
都会重新组合

enter image description here

为什么会出现这种情况?


我使用以下依赖项:

[versions]

agp = "8.3.2"
kotlin = "1.9.0"
coreKtx = "1.15.0"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3"
composeBom = "2024.11.00"
android android-jetpack-compose compose-recomposition
1个回答
0
投票

虽然像

Math.random()
这样的函数不能触发重组,但它可以导致重组,如果:

  • 该可组合项位于正在重组的范围内
  • 它改变可组合项的输入

正如文档所说:

每个可组合函数和 lambda 都可以自行重组。

这意味着当您使用

pseudoState
更改
LaunchedEffect(Unit)
值时,Compose 没有理由触摸除
Button
函数范围之外的任何内容,因为那是唯一
pseudoState
消费者所在的位置。如果你在那里添加一个随机的
Text
,它将被重新组合:

var pseudoState by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {}
Column {
    Button(onClick = {
        pseudoState = !pseudoState
    }) {
        Column {
            Text(text = "TOGGLE pseudoState to ${!pseudoState}")
            Text(text = "Random:: ${Math.random()}")    // recomposed on click
        }
    }

Text
被重构,因为当 Compose 评估其输入时,
Math.random()
被调用并且
text
发生变化。例如,如果我们添加这样的函数:

private fun getSameNumber(): Int {
    println("getSameNumber ${System.currentTimeMillis()}")
    return 7
}

并将

Math.random()
替换为
getSameNumber()
,我们会看到每次点击都会调用
getSameNumber
,但
text
不会改变,并且
Button
不会被重组。


当您更改为

LaunchedEffect(pseudoState)
时,基本上会发生相同的情况,但在
Surface
的范围内。
Column
是一个
inline
函数,不会创建作用域。您可以使用其他一些可组合项创建嵌套范围,例如另一个
Button
:

Surface(
    modifier = Modifier.fillMaxSize().safeDrawingPadding(),
    color = MaterialTheme.colorScheme.background
) {
    var pseudoState by remember { mutableStateOf(false) }
    LaunchedEffect(pseudoState) {}
    Column {
        Button(onClick = {
            pseudoState = !pseudoState
        }) {
            Column {
                Text(text = "TOGGLE pseudoState to ${!pseudoState}")
            }
        }
        Text(text = "Random: ${Math.random()}")
        Text(text = "Same: ${getSameNumber()}")     // Same input, doesn't recompose
        Button(onClick = {}) {
            Text(text = "Random: ${Math.random()}")     // Another scope, doesn't recompose
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.