我可以在 Compose 示例项目中将 ProduceState 替换为 mutableStateOf 吗?

Hel*_*oCW 3 android android-jetpack-compose

以下代码A来自项目

  1. uiState是由委托创建的produceState,我可以使用它mutableStateOf来代替吗produceState?如果是这样,我该如何编写代码?

  2. 为什么我在项目中不能使用代码B?

代码A

@Composable
fun DetailsScreen(
    onErrorLoading: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: DetailsViewModel = viewModel()
) {
    val uiState by produceState(initialValue = DetailsUiState(isLoading = true)) {
        val cityDetailsResult = viewModel.cityDetails
        value = if (cityDetailsResult is Result.Success<ExploreModel>) {
            DetailsUiState(cityDetailsResult.data)
        } else {
            DetailsUiState(throwError = true)
        }
    }
   when {
         uiState.cityDetails != null -> {
    ...
}


@HiltViewModel
class DetailsViewModel @Inject constructor(
    private val destinationsRepository: DestinationsRepository,
    savedStateHandle: SavedStateHandle
) : ViewModel() {

    private val cityName = savedStateHandle.get<String>(KEY_ARG_DETAILS_CITY_NAME)!!

    val cityDetails: Result<ExploreModel>
        get() {
            val destination = destinationsRepository.getDestination(cityName)
            return if (destination != null) {
                Result.Success(destination)
            } else {
                Result.Error(IllegalArgumentException("City doesn't exist"))
            }
        }
}


data class DetailsUiState(
    val cityDetails: ExploreModel? = null,
    val isLoading: Boolean = false,
    val throwError: Boolean = false
)
Run Code Online (Sandbox Code Playgroud)

代码B

@Composable
fun DetailsScreen(
    onErrorLoading: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: DetailsViewModel = viewModel()
) {

    val cityDetailsResult = viewModel.cityDetails
    val uiState=if (cityDetailsResult is Result.Success<ExploreModel>) {
        DetailsUiState(cityDetailsResult.data)
    } else {
        DetailsUiState(throwError = true)
    }

    ...
Run Code Online (Sandbox Code Playgroud)

Age*_*ntP 5

uiState 是由委托 ProduceState 创建的,我可以使用 mutableStateOf 代替 ProduceState 吗?如果是这样,我该如何编写代码?

不,你不能使用mutableStateOf(直接初始化不可能)来编写它。为了理解为什么它不可能,我们需要理解produceState

根据此处提供的文档

ProduceState 启动一个作用范围为 Composition 的协程,可以将值推送到返回的 State 中。使用它将非 Compose 状态转换为 Compose 状态,例如将 Flow、LiveData 或 RxJava 等外部订阅驱动状态引入 Composition。

所以基本上它是一种将非 Compose 状态转换为组合状态的组合方式。

如果你仍然想使用mutableStateOf你可以这样做

var uiState = remember { mutableStateOf(DetailsUIState())}

            LaunchedEffect(key1 = someKey, block = {
                uiState = if (cityDetailsResult is Result.Success<ExploreModel>) {
                    DetailsUiState(cityDetailsResult.data)
                } else {
                    DetailsUiState(throwError = true)
                }
            })
Run Code Online (Sandbox Code Playgroud)

注意:这里的 someKey 可能是另一个处理状态重组的变量

这种方法有什么问题吗?

正如您所看到的,它需要另一个变量someKey来重新组合。与相比,处理它是相当困难的produceState

为什么我在项目中不能使用代码B?

代码B的问题是你在显示结果时不知道数据是否加载。它不是在观察 viewModel 的数据,而是只是获取当前可用的数据,并基于该数据给出组合。

想象一下,如果 viewModel 现在正在获取数据,您将拥有 UiState,isLoading = true但一段时间后,您会在成功的 API 调用后获取数据,或者如果失败则出错,此时这种情况下的可组合函数根本DetailsScreen不知道它,除非您正在观察此组合上方某处的 Ui 状态,并导致此组合根据可用的 newState 进行重组。

但是produceState一旦暂停的网络调用完成,ui的状态就会自动改变......