【问题标题】:Using Jetpack Compose State delegate syntax with data class将 Jetpack Compose State 委托语法与数据类一起使用
【发布时间】:2021-11-03 09:13:25
【问题描述】:

我真的很喜欢将by 委托与Compose State 一起使用,因为它隐藏了具有“反应性”数据的样板,即:

val message: String by remember { mutableStateOf("") }

message = "No boilerplate"

以这种方式使用 props 时,单向数据流(或 props down,event up)是干净且容易的。

但是,当想要在 data class 中将数据组合在一起,或将视图模型道具作为状态观察(即:使用 observeAsAState())时,我必须声明 State 类型的道具,例如:

data class MyUiProps(
  val message: State<String>
)

/*
 * Different ways of creating instances of `MyUiProps`.
 * 
 * In the examples the result is of type `State` rather than
 * `String` like using the `by` delegate
 *
 */
var props = MyUiProps(viewModel.message.observesAsState(""))
props = MyUiProps(remember { mutableStateOf("") })

props.message.value = "Urgh boilerplate"

有没有办法将by 委托与如下伪代码类似的数据类一起使用

data class MyUiProps(
  val message: String
)

val props = MyUiProps(
  message = by remember { mutableStateOf("") }
)

props.message = "Yay no boilerplate"

【问题讨论】:

    标签: android kotlin android-jetpack-compose android-jetpack


    【解决方案1】:

    只需使用MyUiProps 作为您在 ViewModel 中公开的状态。公开单个字段既乏味又不可读。

    如果你在 ViewModel 中有这样的东西:

    // Expose whole state in ViewModel
    private val _state = MutableStateFlow(MyUiProps()) // this will set default values in the model
    val state: StateFlow<MyUiProps> = _state // observe this as state in compose
    
    // inside ViewModel if you want to update the state
    _state.value = _state.value.copy(fieldYouWantToUpdate = newValue)
    
    // in composable
    val state by viewModel.state.collectAsState()
    

    【讨论】:

    • 这适用于简单的应用程序/示例。但是在复杂的应用程序中,将视图模型与耦合到特定 UI 控件/可组合的数据结构混淆会导致问题。 Components/Composables 应该尽可能自包含,定义需要的数据和它们发出的事件。视图模型可以根据需要调整数据并响应来自 Composables 的事件。
    • @kierans 我同意这一点,但我不明白这是如何在这里耦合的。 ViewModel 定义屏幕的状态,compose 是基于该状态渲染 UI。我的标准做法是在可组合的顶部收集AsState,然后传递更专业的可组合所需的参数
    • ViewModel 持有 UI 使用但不拥有的状态。它不应该保持 UI 状态。例如,ViewModel 保存客户资料。它不应该保持按钮是否启用的状态。我的“道具”概念(我从 Vue 抽出来的)是将ViewModel 状态映射到组件/可组合特定状态。所以在ViewModel中存储一个实例是不合适的
    【解决方案2】:

    你不能在构造函数中使用by。尽管如此,您仍然不需要您展示的所谓的“样板”。这就是你的做法:

    data class MyUiProps(
        var message: MutableState<String>
    ) {
    
    }
    
    val props = MyUiProps(
        message = remember { mutableStateOf("") }
    )
    
    props.message.value = "Yay no boilerplate"
    

    您需要在数据类参数中使用MutableState 而不仅仅是State。这避免了使用observeAsState

    【讨论】:

    • 谢谢@Johann。我编辑了我的示例以阐明我想要做什么。但是您已经回答了我的问题,因为数据类中的属性必须是 State/MutableState 类型
    猜你喜欢
    • 1970-01-01
    • 2020-08-30
    • 2021-01-05
    • 1970-01-01
    • 2016-09-13
    • 1970-01-01
    • 2010-10-09
    • 2017-04-16
    • 2011-03-23
    相关资源
    最近更新 更多