如何防止 Jetpack Compose 中不必要的重组

nim*_*ima 8 kotlin android-jetpack-compose compose-desktop

我有一个TextField用于搜索查询和一个Button将执行搜索的查询,结果显示在列中。由于搜索需要几秒钟才能运行,我希望它在按下按钮时执行,而不是在文本更改时执行。

这是一个简化的演示:

Column {
    val list = remember { mutableStateListOf<String>() }
    val textFieldValue = remember { mutableStateOf(TextFieldValue("")) }

    TextField(
        value = textFieldValue.value,
        onValueChange = { textFieldValue.value = it }
    )

    Button({
        list.clear()
        list.addAll(textFieldValue.value.text.split(""))
    }) {
        Text("Search")
    }

    list.forEach {
        println("test")
        Text(it)
    }
}
Run Code Online (Sandbox Code Playgroud)

第一次按下按钮后,foreach 循环将在文本更改时运行。即使单击TextField也会重新运行循环。这不会在文本更改时运行搜索,而是重新呈现结果,这会导致在文本字段中输入时出现故障。

如何防止这种情况发生?

Phi*_*hov 13

上述情况对于 Jetpack Compose 来说是正确的。作者想了解 Compose Desktop,但目前还不太一样,因为它还处于 alpha 阶段,还没有进行太多优化。

修改一个mutableState值总是会导致所有视图的重组,即其读取值。

Any changes to value will schedule recomposition of any composable functions that read value. 文档

阻止它的方法是将所有读取mutableState值的视图移出到单独的视图中。每次值更改都会重新组合mutableState,但不会影响容器。

在您的情况下,这非常简单:只需移动TextField并传递textFieldValue到新函数即可。您可以转发您需要的所有参数,例如modifiertextStyle等。

@Composable
fun TestView(
) {
    Column {
        val textFieldValue = remember { mutableStateOf(TextFieldValue("")) }
        val list = remember { mutableStateListOf<String>("test") }

        TextField(textFieldValue)

        Button({
            list.clear()
            list.addAll(textFieldValue.value.text.split(""))
        }) {
            Text("Search")
        }

        list.forEach {
            println("test $it")
            Text(it)
        }
    }
}

@Composable
fun TextField(
    textFieldValue: MutableState<TextFieldValue>,
) {
    TextField(
        value = textFieldValue.value,
        onValueChange = { textFieldValue.value = it }
    )
}
Run Code Online (Sandbox Code Playgroud)

我不确定为什么没有具有这种语义的系统函数,但在组合中他们更喜欢状态提升模式来匹配 UDF。