当Firebase Firestore中发生更改时,KotlinViewModel不会更新

v7pvogib  于 2023-05-18  发布在  Kotlin
关注(0)|答案(2)|浏览(159)

我有一个访问数据库(Firebase)和统计文档的ViewModel。数据库的结构如下:收藏“帖子”-->文档-->收藏“喜欢”-->文档。因此,我访问集合“Posts”,然后对于每个包含我用户名的文档,我访问集合“Likes”,并获得快照大小来计算有多少个喜欢。问题是,当创建新的Like文档时,ViewModel在应用重新启动之前不会获得更新。

**我使用ViewModel的方式:**我有一个导航图,在那里我创建了一个ViewModel的示例,然后我调用检索数据的函数,然后我将这个示例作为参数传递到需要数据的另一个屏幕中。在另一个屏幕中,我访问存储数据的变量。StateFlow中存储数据的变量(因为我传递的是计数器值而不是文档)
编码(ViewModel):

class PointsViewModel : ViewModel() {
    private val firebase = Firebase.firestore
    private var _points = MutableStateFlow(0)
    val points: StateFlow<Int> = _points

    fun calculate(username: String){
        firebase.collection("Posts")
            .whereEqualTo("PostUser",username)
            .get()
            .addOnSuccessListener { posts ->
                for(post in posts){
                    post.reference.collection("Likes").get().addOnSuccessListener { likes ->
                        _points.value += likes.size()
                    }
                }
            }
    }
}

代码(我在NavGraph中创建示例的地方):

val pointsViewModel: PointsViewModel = viewModel()

//sharedViewModel contains the username
sharedViewModel.user?.username?.let { pointsViewModel.calculate(it) }

代码(我需要数据的地方):

fun AwardsScreen(sharedViewModel: SharedViewModel,pointsViewModel: PointsViewModel){
    
    val points by pointsViewModel.points.collectAsState()
    

    Column(modifier = Modifier
        .fillMaxWidth()
        .padding(15.dp, 15.dp)) {

        Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
           Text(text = points.toString())
        }
    }

我试着在屏幕内做所有这些操作我需要数据。因此,我尝试访问数据库并从中检索所有数据,添加一个snapshotListener来获取更新,但没有成功

3pmvbmvn

3pmvbmvn1#

这不是你应该做的时候,当涉及到计数器在Firestore。当您像现在这样计算文档时,查询返回的每个文档都会花费您一次读取,当涉及到更多的喜欢时,这可能会很昂贵。不幸的是,count()不提供实时更新。但是,如果您自己创建和维护一个计数器,它将更便宜。它可以简单地是文档中存在的number类型的字段。这意味着每次向集合中添加新文档时,您只需增加计数器,并在删除文档时减少计数器。在此文档中,您可以附加一个实时侦听器,您将拥有一个始终处于最新状态的计数器。这意味着,为了显示喜欢的数量,你只需要为一次阅读付费。
这样的操作不应该在客户端代码中执行,而应该在可信的环境中执行。所以我建议你在Cloud Functions for Firebase中写一个函数,它可以自动为你完成这个任务。

hyrbngr7

hyrbngr72#

首先,只是一个小问题,_points可以是val,实际上应该是,因为你不想改变实际示例,只想改变它的值。
我注意到的另一件事是,您使用的是+=操作数,这可能是这里的罪魁祸首。尝试分配一个新的值,如_points.value = _points.value + likes.size()
医生说
对于赋值操作,例如a += B,编译器执行以下步骤:
如果右列中的功能可用:
如果相应的二进制函数(意味着plusAssign()的plus())也可用,则a是可变变量,并且plus的返回类型是a类型的子类型,则报告错误(歧义)。
确保其返回类型为Unit,否则报告错误。
生成.plusAssign(B)的代码。
否则,尝试为a = a + B生成代码(这包括类型检查:a + B的类型必须是a)的子类型。
它可能需要一个全新的值,而不需要在赋值中引用自身。
最后,我会建议使用状态保持器。所以你会有一个这样的类:

data class StateHolder(val points: Int = 0)

你可以在viewModel中这样使用它:

class PointsViewModel : ViewModel() {
    private val firebase = Firebase.firestore
    private val _stateHolder = MutableStateFlow(StateHolder())
    val stateHolder: StateFlow<StateHolder> = _stateHolder

    fun calculate(username: String){
        firebase.collection("Posts")
            .whereEqualTo("PostUser",username)
            .get()
            .addOnSuccessListener { posts ->
                for(post in posts){
                    post.reference.collection("Likes")
                        .get()
                        .addOnSuccessListener { likes ->
                            _stateHolder.value = stateHolder
                                .value
                                .copy(points =  
                                    stateHolder.value.points + likes.size())
                    }
                }
            }
    }
}

最后,观察它在一个组合:

val stateHolder by pointsViewModel.stateHolder.collectAsState()
    

    Column(modifier = Modifier
        .fillMaxWidth()
        .padding(15.dp, 15.dp)) {

        Row(modifier = Modifier.fillMaxWidth(), 
            horizontalArrangement = Arrangement.Center) {
           Text(text = "${stateHolder.points}")
        }
    }

相关问题