Compose 函数在将状态值传递给另一个函数时会进行重构

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

我似乎没有找到这个简单案例的解释。 一些简单的东西,比如持有逆流的视图模型:

class TestCounter : ViewModel() {
    private val _counter = MutableStateFlow(0)
    val counter = _counter.asStateFlow()

    init {

        viewModelScope.launch {

            while (true){
                delay(1000)
                _counter.update {
                    it + 1
                }
            }
        }
    }
}

还有一个可组合函数,每次将其收集的状态值传递给另一个函数时,它都会自行重组。如果我将状态对象本身传递给另一个函数,则不会进行重组。虽然我读到这不是最佳实践。

@Composable
fun App() {

    val testCounter = TestCounter()

    val counter = testCounter.counter.collectAsState()

    SideEffect {
        println("RECOMPOSING EVERY TIME IF PASSING VALUE")
    }

    DrawCount(counter.value)
//  DrawCount(counter)

}

我想念这里的一些东西。如果这是正常行为,那么我将如何使用委托(by)或用于 UI 状态的数据类来保存不同函数的一堆值。它总是会导致不必要的重组。 值稳定与否并不重要。

android kotlin android-jetpack-compose compose-recomposition
1个回答
0
投票

规则很简单:每个读取 State 对象值的可组合项都会在该值发生变化时再次执行。

您的

App
可组合项读取
counter
状态的值,以便将其传递给
DrawCount
。当
counter
每秒都在变化时,那么
App
每秒都需要重新组合。一切都很好。如果您将权力委托给国家并不重要,这只会在幕后做同样的事情。

但这并不是不必要的,因为每次重组时

DrawCount
都会使用新值再次执行。这很重要,否则该可组合项将无法更新。

您可以将代码包装在

remember
语句中以在重组期间跳过它。这就是
collectAsState
在内部完成的操作,因此实际上不会第二次收集流量。

唯一需要关心的是如何创建视图模型。通过使用

TestCounter()
,您可以在每次重组时(在本例中为每秒)创建一个新实例。计数器再次启动(从0开始),旧的计数器与旧的视图模型一起被丢弃。

您想要的是始终获得相同的视图模型实例。将其包装在

remember
中是不够的,因为视图模型需要比可组合项的寿命更长。所有被
remember
编辑的东西只能在重组后存活,当可组合项离开组合时它就不会存活。但是,视图模型应该仍然相同,以便当可组合项再次进入合成时,您将获得上次获得的相同实例。

这就是为什么 Compose 框架为您提供了一个特殊的

viewModel()
函数来为您完成所有这些工作。

如果您需要

TestCounter
的实例,正确的方法是:

val testCounter: TestCounter = viewModel()
© www.soinside.com 2019 - 2024. All rights reserved.