【问题标题】:Why Compose remembers a state of an object rather than the object itself?为什么 Compose 记住对象的状态而不是对象本身?
【发布时间】:2022-06-13 15:51:35
【问题描述】:

我了解到 Compose 以如下方式记住状态:

var text by remember { mutableStateOf("") }

所以在这种情况下,它会记住一个字符串的 MutableState。我的问题是为什么它要记住一个叫做“MutableState”的东西而不仅仅是字符串本身,为什么它需要一个额外的层?

【问题讨论】:

    标签: android-jetpack-compose


    【解决方案1】:

    remember 用于存储对象以在发生重组时拥有它。可变状态用于触发重组,您可以查看this answer了解更多详情。

    by 是 delegation,这是 Kotlin 的一个功能,可以翻译代码

    var text = remember { mutableStateOf("") }
    text.value = "newString"
    

    您基本上将触发器和值存储在记忆中。当您更改 MutableState.value 时,会发生新的重组,在这个新的重组中,您将获得 MutableState 的最新值。

    当其他东西触发重组(例如画布触摸位置)时,也有不需要 MutableState 的记住用例,例如 Paint自定义对象

    你记得对象,因为你不会实例化它。

    val paint = remember {Paint()}
    var offset by remember {mutableStateOf(Offset.Zero)
    

    然后当偏移量随着用户触摸屏发生变化时,您会触发重组,但因为您不需要再次实例化 Paint 对象。

    remember only 和 remember with MutableState 有不同的用例。

    【讨论】:

      【解决方案2】:

      需要可变状态有两个原因:

      1. 在重组之间保存可变状态。 remember 将保存 lambda 计算的结果,但如果您稍后更改变量 - remember 无法保存和跟踪它。解决方案是拥有一个状态持有者 - 由mutableStateOf 创建、由remember 保存的对象将始终相同,但您可以更改它的属性,在本例中为value(当您'重新使用by 的委托)。
      2. 触发重组。如果您只是创建一个类,使用remember 保存它并更新一个属性,Compose 不会知道它已更改,并且需要更新它的视图 - 这就是创建特殊 Compose State 的原因,它通知视图它需要重新组合。

      您可以通过 Compose documentationThinking in Compose 中的状态继续加深您的知识。

      【讨论】:

        【解决方案3】:

        我知道已经很晚了,我会尝试与remember分享我学到的东西。

        我有一个迷你待办事项应用程序,我在其中使用jetpack compose 练习我的学习,使用viewwmodel 提升的待办事项列表使用SnapshotStatelist,此列表由LazyColumn 呈现,其中每个待办事项model有它自己的rememberedstate,在那里我做了一些非常基本的 UI 功能(例如卡片提升、某些图标的可见性),我对 todo 所做的任何更改都应该传播回mutableStateList,例如当 todo 是deleted,从列表中删除,SnapshotStateList 然后会自动通知LazyColumn 执行recomposition,但是当我edit 一个Todo 时(比如,修改标题作为基本todo 功能的一部分),我还必须更新保存此待办事项的itemcomposable(一些UI更改),然后我被困住了,因为我无法弄清楚为什么itemcomposable不是recomposing,即使我能够使用以下代码验证 SnapShotStateList 项目是否已修改

        val todoList = viewModel.todos
        val snapshot = Snapshot.takeMutableSnapshot()
        snapshot.enter {
            for (todo in todoList) {
                Log.e("TodoModel", todo.title)
            }
        }
        

        我验证了我对 todo 所做的任何 修改 都会反映到它的主机列表中,但是呈现 todo 的 item composable 不会触发重新组合。继续阅读一些帖子并仔细思考对象引用,我认为一定有一些东西保持item 的先前状态并且更改不适用于remember,直到我发现您可以提供keyremember,它将决定remember 是否需要重新计算。现在我发现remember 只记得 initial composition 上的一个状态(状态我不是指组合状态,而是一般的状态),它将保持该初始结构/状态只要由于它所属的整个可组合组件仍在运行,在这种情况下一直到父 composable(即我的 DashboardScreen),是我提供了我的 remember re-calculate带有 todo 对象本身的键

        val itemState: ItemCardState = remember(key1 = todoModel) {
            ItemCardState(todoModel = todoModel)
        }
        

        这样,当SnapShotStateList 发生更改时,每个itemremember 将看到相同的对象引用(数据类),但应用了更改。 remember 缓存初始状态并将永久保存,除非您提供一个您认为可能会更改的密钥,并让 remember 重新计算一个新的 initial 状态以被记住。

        现在有了这种理解,我无法想象没有层可以容纳对象 (remember) 并在对象状态发生变化时防止不必要的重新组合。

        只是分享我学到的东西,也可以讨论我可能以错误的方式说的。

        【讨论】:

          猜你喜欢
          • 2014-08-03
          • 1970-01-01
          • 2013-08-19
          • 2022-10-24
          • 1970-01-01
          • 2022-10-23
          • 1970-01-01
          • 1970-01-01
          • 2012-10-25
          相关资源
          最近更新 更多