我很难理解为什么我观察到的状态值被正确地传递到可组合的lambda中,但没有触发可组合的重新组合。
这是我的设置。
屏幕
@Composable
fun Screen(
showBottomSheet: (@Composable () -> Unit) -> Unit,
viewModel: MyViewModel = hiltViewModel()
) {
val someValue = viewModel.someValue.observeAsState().value
MyController(
onShowBottomSheetClick = {
showBottomSheet {
Log.d("DEBUG", "value updated: $someValue")
BottomSheetLayout(
someValue = someValue,
onUpdateClick = { newValue -> viewModel.setValue(newValue) }
)
}
}
)
}
MyController
@Composable
fun MyController(
onShowBottomSheetClick: () -> Unit
) {
Text(
text = "show BottomSheet",
modifier = Modifier.clickable { onShowBottomSheetClick() }
)
}
底部工作表布局
@Composable
fun BottomSheetLayout(
someValue: Int,
onUpdateClick: (Int) -> Unit
) {
Text(
text = "Some value: $someValue",
modifier = Modifier.clickable { onUpdateClick(someValue + 1) }
)
}
MyViewModel
@HiltViewModel
class MyViewModel @Inject constructor(): ViewModel() {
private val _someValue: Int? = MutableLiveData(null)
val someValue: LiveData<Int?> = _someValue
fun setValue(newValue: Int) {
_someValue.value = newValue
}
}
当我运行这个并看到输出时,日志不会显示更新的someValue
。
value updated: 0
value updated: 0
value updated: 0
但是,如果我向lambda本身添加一个新的状态值,它就可以正常工作。
// Screen
// ..
showBottomSheet {
val currentValue = viewModel.someValue.observeAsState().value // <-- Note the difference
Log.d("DEBUG", "value updated: $currentValue")
BottomSheetLayout(
someValue = currentValue,
onUpdateClick = { newValue -> viewModel.setValue(newValue) }
)
}
// ..
当我运行这个并看到输出时,日志会显示正确更新的值。
value updated: 0
value updated: 1
value updated: 2
为什么会发生这种情况?有没有什么方法可以将第一个状态值传递给showBottomSheet
可组合lambda?
提前谢谢。
我还没有尝试过你的代码,但这看起来像是这个问题中作用域重组的效果。在这个问题中,即使不记得mutableState值也会更新,因为外部作用域没有重新组合。在您的情况下,因为您传递的是值,而不是由于未触发重组而未更新的状态。
为什么没有记忆的可变状态有时有效?
@Composable
fun Screen(
showBottomSheet: (@Composable () -> Unit) -> Unit,
viewModel: MyViewModel = hiltViewModel()
) {
val someValue = viewModel.someValue.observeAsState().value
MyController(
onShowBottomSheetClick = {
showBottomSheet {
Log.d("DEBUG", "value updated: $someValue")
BottomSheetLayout(
someValue = someValue,
onUpdateClick = { newValue -> viewModel.setValue(newValue) }
)
}
}
)
}
在该代码中,只有showBottomSheet
被重新组合,因此作为值的someValue不会更新,因为MyController
范围中没有重新组合。
有了这段代码,我假设showBottomSheet
是可组合的,因为你可以调用@Composable函数Flow.collectAsState,你正在重新组合里面的所有东西,这就是为什么它用viewModel.someValue
的lates值重新组合的原因
showBottomSheet {
val currentValue = viewModel.someValue.observeAsState().value // <-- Note the difference
Log.d("DEBUG", "value updated: $currentValue")
BottomSheetLayout(
someValue = currentValue,
onUpdateClick = { newValue -> viewModel.setValue(newValue) }
)
}
使用代理
这将以与remember API
相同的方式将您的值包装到State<T>
,因此每次您的值更改时都会导致重新组合。
val someValue by viewModel.someValue.observeAsState()
在您的示例中,由于.value
和未调用recomposition,状态立即转换。
Log.d是一个副作用,应该与它的一个处理程序一起使用。
阅读更多关于Jetpack Compose副作用
使用LaunchedEffect或DisposableEffect
@Composable
fun Screen(
showBottomSheet: (@Composable () -> Unit) -> Unit,
viewModel: MyViewModel = hiltViewModel()
) {
val someValue = viewModel.someValue.observeAsState().value
LaunchedEffect(someValue) {
Log.d("DEBUG", "value updated: $someValue")
}
MyController(
onShowBottomSheetClick = {
showBottomSheet {
BottomSheetLayout(
someValue = someValue,
onUpdateClick = { newValue -> viewModel.setValue(newValue) }
)
}
}
)
}
每次你的值改变时,效果就会开始