简体   繁体   English

Jetpack Compose 将 LiveData 传递给可组合 lambda

[英]Jetpack Compose passing LiveData to a Composable lambda

I'm having trouble understanding why my value I'm observing as state is properly passed into a Composable lambda, but does not trigger that composable to recompose.我无法理解为什么我观察到我的值,因为 state 已正确传递到可组合 lambda 中,但不会触发可组合重构。

Here's my setup.这是我的设置。

Screen屏幕

@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() }
    )
}

BottomSheetLayout底页布局

@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
    }
}

When I run this and see output, the log doesn't show updated someValue .当我运行它并看到 output 时,日志没有显示更新的someValue

value updated: 0
value updated: 0
value updated: 0

However, if I add a new state value to the lambda itself, it work fine.但是,如果我将新的 state 值添加到 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) }
        )
    }
// ..

When I run this and see output, the log shows properly updated value.当我运行它并看到 output 时,日志显示正确更新的值。

value updated: 0
value updated: 1
value updated: 2

Why is this happening?为什么会这样? Is there any way I can pass the first state value to the showBottomSheet composable lambda?有什么办法可以将第一个 state 值传递给showBottomSheet可组合 lambda?

Thank you in advance.先感谢您。

Log.d is a Side-Effect and should be used with one of its handler. Log.d 是一个副作用,应该与它的一个处理程序一起使用。

read more about Jetpack Compose Side-Effects 阅读有关 Jetpack Compose 副作用的更多信息

Use LaunchedEffect or DisposableEffect使用 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) }
                )
            }
        }
    )
}

The effect will be started every time your value changes每次您的值更改时都会启动效果

I haven't tried your code but this looks like an effect of scoped recomposition as in this question.我没有尝试过你的代码,但这看起来像是这个问题中范围重组的效果。 In that question even without remember mutableState value is updated because outer scope wasn't recomposed.在那个问题中,即使没有记住 mutableState 值也会更新,因为外部 scope 没有重组。 In your case since you are passing value instead of state that is not updated because of not triggering recomposition.在您的情况下,因为您传递的是 value 而不是 state 因为没有触发重组而没有更新。

Why does mutableStateOf without remember work sometimes? 为什么没有记忆的 mutableStateOf 有时会起作用?

@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) }
                )
            }
        }
    )
}

In this code only showBottomSheet is recomposed and because of that someValue which is a value is not updated since there is no recomposition in MyController scope.在此代码中,仅重构了showBottomSheet ,并且由于MyController scope 中没有重构,因此作为值的 someValue 未更新。

With this code, i assume showBottomSheet is a Composable since you are able to call @Composable function Flow.collectAsState you are recomposing everything inside and that's why it's recomposed with lates value of viewModel.someValue使用此代码,我假设showBottomSheet是可组合的,因为您可以调用 @Composable function Flow.collectAsState 您正在重新组合内部的所有内容,这就是为什么它使用viewModel.someValue的最新值重新组合

showBottomSheet {
    val currentValue = viewModel.someValue.observeAsState().value // <-- Note the difference
    Log.d("DEBUG", "value updated: $currentValue")
    BottomSheetLayout(
        someValue = currentValue,
        onUpdateClick = { newValue -> viewModel.setValue(newValue) }
    )
}

Use the Delegate使用代理

That wraps your value to State<T> the same way as remember API does, thus recomposition will be caused every time your value changes.这将您的值包装到State<T>remember API的方式相同,因此每次您的值更改时都会导致重新组合。

val someValue by viewModel.someValue.observeAsState()

In your example the state immediately transformed because of .value and recomposition was not called.在您的示例中,state 由于.value而立即转换,并且未调用重组。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM