更新喷气背包中的局部变量组成和副作用

Updating local variables in jetpack compose and side effects

提问人:thetwom 提问时间:8/31/2023 更新时间:9/1/2023 访问量:84

问:

阅读有关喷气背包撰写(在撰写中思考)的副作用,我找到了这个例子:

@Composable
@Deprecated("Example with bug")
fun ListWithBug(myList: List<String>) {
    var items = 0

    Row(horizontalArrangement = Arrangement.SpaceBetween) {
        Column {
            for (item in myList) {
                Text("Item: $item")
                items++ // Avoid! Side-effect of the column recomposing.
            }
        }
        Text("Count: $items")
    }
}

如本示例的上下文所述,这是不正确的,因为不允许修改局部变量。现在我想知道,什么是允许的,什么是不允许的。compose 如何知道它可以并行评估?如果将其移动到 -scope 中,是否允许?itemsitem in myListvar itemsColumn

更进一步,是否允许更新变量?我们可以做这样的事情吗:

class LongLastingClass() {
   var someState = mutableStateOf(0) 
      private set
    
   fun update(value: Int) {
      someState = value
   } 
}

@Composable
fun ListWithUpdate(value: Int) {
   val longLastingObject = remember{ LongLastingClass() }
   longLastingObject.update(value) 
   ...
}

还是必须转移到副作用之类的东西上?或者这个结构是一个问题,因为我们最好不要修改可组合项中的状态变量,所以需要副作用?update

android-jetpack-compose 副作用

评论


答:

1赞 chuckj 9/1/2023 #1

暂时忽略它们,因为它们是内联函数并且很特殊,因此您永远不应该在 Compose 中编写依赖于可组合 lambda 何时执行的代码。它可能不会产生您期望的结果。RowColumn

如果将上述内容更改为

@Composable
@Deprecated("Example with bug")
fun ListWithBug(myList: List<String>) {
    var items = 0
    A {
        B {
          for (item in myList) {
            Text("Item: $item")
            items++ // Avoid! Side-effect of the column recomposing.
          }
        }
        Text("Count: $items")
    }
}

仅当每个行调用始终调用调用其 lambda 时,此代码才有效,而调用调用其 lambda。如果 的 lambda 被调用并被跳过,则 的值将在 执行时。换言之,此代码仅在不跳过且传递给的 lambda 不跳过时才有效,无论何时执行。ListWithBugABABitems0Text("Count: $items")BBListWithBug

此代码错误不需要并行性,只需跳过即可。

这样就不用Compose来计数了。如果您将其更改为

@Composable
fun WorkingList(myList: List<String>) {
    A {
        B {
          for (item in myList) {
            Text("Item: $item")
          }
        }
        Text("Count: ${myList.size}")
    }
}

它产生相同的结果,而不依赖于何时或是否执行可组合的 lambda。

评论

0赞 thetwom 9/4/2023
这使事情变得更加清晰。因此,这意味着将 移动到 -scope(忽略它是一个内联函数)将使代码有效。第二个示例中描述的变量更新也是正确的代码。右?var items = 0Column
0赞 chuckj 9/6/2023
代码可以工作,但我仍然不认为这个代码有效。一般来说,不要在可组合函数中使用 var 局部变量,因为它几乎从来都不是正确的,而且在它工作的情况下,对函数的任何更改都极有可能导致它将来中断。切勿使用组合来生成中间结果,因为这些结果可能无效,因为组合函数不能保证运行通常的序列。改为在视图模型中生成完整的结果,并使用组合来可视化这些结果。
0赞 thetwom 9/7/2023
谢谢。我理解状态逻辑应该保留在外面。归根结底,这更多的是为了理解。如果涉及到状态之间的动画转换,我想了解如何在可组合对象中更新状态很重要......
1赞 chuckj 9/8/2023
" ...了解如何更新可组合项中的状态非常重要“:不要更新可组合项中的状态。使用协程或其他机制来更新 compose 将响应的状态。