开发者问题收集

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

2021-08-03
12216

我有一个用于搜索查询的 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)
    }
}

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

如何防止这种情况?

3个回答

以上内容适用于 Jetpack Compose。作者想了解 Compose Desktop,但它还不是一样,因为它处于 alpha 阶段,尚未进行太多优化。

修改 mutableState 值始终会导致所有视图重组,并读取其值。

对值的任何更改都将安排读取值的任何可组合函数的重组。 文档

阻止它的方法是将所有读取 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 }
    )
}

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

Phil Dukhov
2021-08-04

我不喜欢移动 mutableState&lt;&gt; 作为参数 就像我一样切勿使用 livedata&lt;&gt; 作为参数。相反,您可以将阅读变成lambda:

520001321
Jemshit
2021-09-12
Column {
    val list = remember { mutableStateListOf<String>() }
    var textFieldValue = remember { mutableStateOf(TextFieldValue("")) }
    var searchTerm = remember { textFieldValue.value.text.copy() }

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

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

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

试试这个

Richard Onslow Roper
2021-08-03