下面是导致无限重组问题的代码
主要活动
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
val viewModel : MainViewModel by viewModel()
val state by viewModel.state.observeAsState()
NavHost(navController = navController, startDestination = "firstScreen") {
composable("firstScreen") { FirstScreen(
navigate = {
navController.navigate("secondScreen")
}, updateState = {
viewModel.getState()
},
state
)}
composable("secondScreen") { SecondScreen() }
}
}
}
}
检视模型
class MainViewModel : ViewModel() {
//var state = MutableStateFlow(0)
private val _state = MutableLiveData(0)
val state: LiveData<Int> = _state
fun getState() {
_state.value = 1
}
}
第一个屏幕
@Composable
fun FirstScreen(
navigate: () -> Unit,
updateState: () -> Unit,
state: Int?
) {
Log.e("state",state.toString())
Button(onClick = {
updateState()
}) {
Text(text = "aaaaaaaa")
}
if(state == 1) {
Log.e("navigate",state.toString())
navigate()
}
}
第二个屏幕
@Composable
fun SecondScreen() {...}
按下该按钮可更改视图模型中的状态,并且作为React,如果该按钮更改为1,则会触发导航至第二个屏幕,但第一个屏幕会无限重组并阻止整个过程
编辑
@Composable
fun FirstScreen(
navigate: () -> Unit,
updateState: () -> Unit,
state: Int?
) {
Log.e("state",state.toString())
Button(onClick = {
updateState()
}) {
Text(text = "aaaaaaaa")
}
LaunchedEffect(state) {
if (state == 1) {
Log.e("navigate", state.toString())
navigate()
}
}
}
这就解决了问题
1条答案
按热度按时间kr98yfug1#
这是因为你是基于一个条件属性来导航的,这个属性是
FirstScreen
可组合函数的一部分,对这个属性的更改超出了FirstScreen's
的作用域,如果这个条件属性的值没有改变,那么每次NavHost
更新时,它总是会计算它的块,在你的例子中,state
仍然是1
,并且总是会执行它的块。您所经历的事件可总结如下:
FirstScreen
和SecondScreen
(初始值为NavHost
和composition
)FirstScreen
观察值为0
的整数state
state
变为1
FirstScreen
re-composes
,满足条件(state==1
),执行**1st
**时间的导航re-composes
FirstScreen's
state
仍为1
,仍满足条件(state==1
),再次执行导航***********次re-composes
FirstScreen's
state
保持1
,满足条件(state==1
),再次执行导航**3rd
**次基于官方的Docs,
您应该只将navigate()作为回调的一部分调用,而不是作为可组合对象本身的一部分调用,以避免在每次重新组合时调用navigate()。
我建议把
navigation
当作一个一次性事件,在LaunchedEffect
内部进行,并从SharedFlow
发射中观察到。下面是一个简短的解决方案。有密封类
UiEvent
、像这样修改你的
ViewModel
,然后通过您的
FirstScreen
中的LaunchedEffect
进行观察请参见my answer here