简体   繁体   English

Kotlin - StateFlow 不向其收集器发送更新

[英]Kotlin - StateFlow not emitting updates to its collectors

I got a StateFlow of type UserStateModel (data class) in my app.我的应用程序中有一个 UserStateModel(数据类)类型的 StateFlow。

private val _userStateFlow: MutableStateFlow<UserStateModel?> = MutableStateFlow(UserStateModel())
val userStateFlow: StateFlow<UserStateModel?> = _userStateFlow

here is the UserStateModel这是用户状态模型

data class UserStateModel(
    val uid: String? = null,
    val username: String? = null,
    val profileImageUrl: String? = null,
    var isLoggedIn: Boolean = false,
    val isPremiumUser: Boolean = false,
    val posts: List<Post>? = listOf()
) 

When I update the StateFlow with a new Username it emits the change to the collectors and the UI updates.当我使用新的用户名更新 StateFlow 时,它会将更改发送给收集器并更新 UI。 But when I change a property inside the posts: List?但是当我更改帖子中的属性时:列表? list it doesnt emit the changes.列出它不会发出更改。 When I change the size of the list it does, when I change the name property of the Post at index 0 it doesnt.当我更改列表的大小时,当我更改索引 0 处的 Post 的 name 属性时,它不会。 How can I detect changes to the child properties of the Data class?如何检测对 Data 类的子属性的更改?

Right now I use an ugly workaround, I add现在我使用一个丑陋的解决方法,我补充说

val updateErrorWorkaround: Int = 0 

to the UserStateModel data class and increase it by one so the collectors get notified到 UserStateModel 数据类并将其增加一,以便收集器得到通知

Ps I'm using MVVM + Clean Architecture and Jeptack Compose Ps 我正在使用 MVVM + Clean Architecture 和 Jeptack Compose

EDIT Thats my Post Model:编辑那是我的帖子模型:

data class Post(
    val id: Int,
    val name: String, 
    val tags: MutableList<Tag>? = null

)

Here is how I update the MutableList:这是我更新 MutableList 的方法:

val posts = userStateFlow.value?.posts
posts.get(index).tags?.add(myNewTag)
_userStateFlow.value = userStateFlow.value?.copy(posts = posts)

Those changes are not emitted to the collectors这些更改不会发送给收集器

StateFlow emits only if it detects changes to the value, it ignores replacing the value with the same data. StateFlow仅在检测到值更改时发出,它忽略用相同数据替换值。 To do this, it compares the previous value with the new one.为此,它将先前的值与新的值进行比较。 For this reason, we shouldn't modify the data that we already provided to the StateFlow , because it won't be able to detect changes.出于这个原因,我们不应该修改我们已经提供给StateFlow的数据,因为它无法检测到变化。

For example, we set value to a User(name=John) .例如,我们将value设置为User(name=John) Then we mutate the same user object by modifying its name to James and we set the value to this "new" user object.然后我们通过将其name修改为James来改变相同的用户对象,并将value设置为这个“新”用户对象。 StateFlow compares "new" User(name=James) with its stored value, which is now also User(name=James) , so it doesn't see any changes. StateFlow将“新” User(name=James)与其存储的值(现在也是User(name=James)进行比较,因此它看不到任何变化。

In your example you created a copy of UserStateModel , but inside you re-use the same objects and you mutate them.在您的示例中,您创建了UserStateModel的副本,但在内部您重复使用相同的对象并改变它们。 In this case you added a new item to tags and this change affected old UserStateModel as well, so StateFlow doesn't detect the change.在这种情况下,您向tags添加了一个新项目,并且此更改也影响了旧的UserStateModel ,因此StateFlow不会检测到更改。

To fix the problem, you need to copy all the data that was changed and do not mutate anything in-place.要解决此问题,您需要复制所有已更改的数据,并且不要在原地更改任何内容。 It is safer to make all the data immutable, so val and List - this way you are forced to make copies.使所有数据不可变更安全,因此valList - 这样您就被迫进行复制。 I changed tags to val tags: List<Tag> = listOf() , then your code could look like the following:我将tags更改为val tags: List<Tag> = listOf() ,那么您的代码可能如下所示:

val posts = userStateFlow.value?.posts!!.toMutableList()
posts[index] = posts[index].copy(tags = posts[index].tags + myNewTag)
userStateFlow.value = userStateFlow.value?.copy(posts = posts)

Here we create a copy of not only UserStateModel .在这里,我们不仅创建了UserStateModel的副本。 We also copy posts list, the Post that we modify and we also copy the list of tags.我们还复制posts列表,我们修改的Post ,我们还复制标签列表。

Alternatively, if this behavior of StateFlow is more annoying to you than helpful, you can use SharedFlow which doesn't compare values, but just emits.或者,如果StateFlow的这种行为对您来说更烦人而不是有用,您可以使用SharedFlow ,它不比较值,而只是发出。

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

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