StateFlow Observer 被触发两次 - 这是一个好的解决方案吗? [英] StateFlow Observer is being triggered Two Times - Is this a good solution?

查看:31
本文介绍了StateFlow Observer 被触发两次 - 这是一个好的解决方案吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,所以我一直在使用带有 Room 数据库的 StateFlow.现在我有一个常见的案例.在我的应用程序开始时,我有一个逻辑,如果 ROOM 数据库为空,我应该显示一个 EmptyContent(),否则我将显示 ROOM 数据库中的 ListContent().

Okay so I've been using StateFlow with Room database for a while. Now I have one common case. At the start of my app I have a logic that if ROOM database is empty I should show an EmptyContent(), otherwise I will show the ListContent() from ROOM database.

现在每次我启动应用程序时,我总是让 EmptyContent() 显示半秒,然后显示 ListContent().之后,当我使用该应用程序时,一切正常.但是在那个应用程序启动时,当 ROOM 数据库正在工作时,我猜 EmptyContent() 只显示一小段时间(因为我的 StateFlow 默认值是一个空列表),然后显示来自数据库的实际 LIST.

Now every time I launch the app, I'm always getting that EmptyContent() shown for a HALF a second maybe, and then the ListContent() is displayed. After that when I'm using the app everything works normal. But at that app launch time, while ROOM database is working I guess, that EmptyContent() is shown for just a small amount of period (Because my StateFlow default value is an empty list), and after that the actual LIST from Database is displayed.

现在我有一个解决方案,只需在协程中使用 delay() 函数,等待例如 200 毫秒,然后触发读取数据库的函数,因为那些 200 毫秒足以让 ROOM 数据库实际获取值并使用实际数据更新我的 STATE FLOW 变量,而不是在开始时使用该 StateFlow 默认值半秒.

Now I have one solution for that, to just use delay() function inside a Coroutine, to wait for example 200MS and then trigger the function for reading the DATABASE, because those 200MS are enough for ROOM database to actually get the value and update my STATE FLOW variable with the actual data instead of using that StateFlow default value for a half second at the beginning.

这是一个很好的解决方案吗,我必须问一下?因为我使用的是协程,线程没有被阻塞,我只是在等待 ROOM 数据库第二次更新我的 STATE FLOW 变量.

Is that a good solution, I must ask? Because I'm using coroutine, the thread is not blocked, and I'm just waiting until ROOM database updates my STATE FLOW variable the second time.

@Composable
fun displayContent(
    tasks: List<ToDoTask>,
    ListContent: @Composable () -> Unit
) {
    val scope = rememberCoroutineScope()
    var counter by remember { mutableStateOf(0)}
    LaunchedEffect(Unit){
        scope.launch {
            delay(200)
            counter = 1
        }
    }
    if(counter == 1){
        if (tasks.isNotEmpty()) {
            ListContent()
        } else {
            EmptyContent()
        }
    }
}

推荐答案

我的建议是映射您的预期状态.例如:

My suggestion would be map your expected states. For instance:

sealed class RequestState<out T> {

    object Idle : RequestState<Nothing>()

    object Loading : RequestState<Nothing>()

    data class Success<T>(val data: T) : RequestState<T>()
    
    data class Error(
        val t: Throwable,
        var consumed: Boolean = false
    ) : RequestState<Nothing>()
}

你的函数应该是这样的:

And your function would be something like:

@Composable
fun YourScreen() {
   val requestState = viewModel.screenData.collectAsState()
   when (requestState) {
       is Idle -> 
           // This is the default state, do nothing
       is Loading -> 
           // Display some progress indicator
       is Success ->
           YourListScreen(requestState.data) // Show the list
       is Error ->
           // Display an error.
   }
   LaunchedEffect(Unit) {
      viewModel.loadData()
   }
}

当然,在您的视图模型中,您必须正确地发出这些值...

Of course, in your view model you must emit these values properly...

class YourView: ViewModel() {
    private val _screenData =
        MutableStateFlow<RequestState<List<ToDoTask>>>(RequestState.Idle)
    val screenDatae: StateFlow<RequestState<List<ToDoTask>>> = _screenData

    fun loadData() {
       _screenData.value = Loading
       try {
           // load the data from database
           _screenData.value = Success(yourLoadedData)
       } catch (e: Exception) {
           _screenData.value = Error(e)
       }
    }
}

这篇关于StateFlow Observer 被触发两次 - 这是一个好的解决方案吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆