在我的Jetpack Compose应用程序中,我使用Room来存储TODO项目列表,每个项目都有一个可编辑的文本。我希望将数据库作为唯一的事实来源(提供任务列表流),但为每个字符leads to issues异步更新DB。
我想使用一个MutableState
来“缓存”文本输入和
- 只要该高速缓存在一段时间内没有改变,就更新DB(可以通过将缓存
MutableState
转换为流并使用debounce
来完成) - 当用户输入文本时该高速缓存,而且当DB更改时也更新缓存。如何做到这一点?
这就是我到现在为止所做的:
@Composable
fun TasksScreen(viewModel = ...) {
val tasks: List<String> by viewModel.tasks.collectAsStateWithLifecycle()
LazyColumn {
items(items = tasks) { index, task ->
Task(text = item, onTextChanged = { viewModel.setText(index, it) })
}
}
}
@Composable
fun Task(text: String, onTextChanged: (String) -> Unit) {
val textState = remember { mutableStateOf("") }.apply { value = text }
val textUpdateFlow = remember {
snapshotFlow { textState.value }.debounce(1000).onEach { onTextChanged(it) }
}
BasicTextField(
value = textState.value,
onValueChange = { textState.value = it },
)
}
问题:每当textCache
被更新时,Task
可组合项就会被重新组合,但这意味着原始文本会覆盖用户输入的文本,因此文本字段保持不变。另外,我不确定在组合中定义和记住textUpdateFlow是否正确,以及流的作用域。
1条答案
按热度按时间zpqajqem1#
这个问题的一个解决方案是使用不同的重组作用域,在与读取文本
MutableState
的作用域不同的作用域中创建文本MutableState
。下面是一个通用代码片段,如果组合项需要一个应该更新的快速缓存状态,并且可以由一个较慢的外部状态(例如a DB)。它可以这样使用:
以下是它的工作原理:
CachedUpdate
可组合组件创建并记住缓存状态。在(重新)合成时,此状态将使用传入CachedUpdate
的最新值进行更新。然后将缓存的状态对象传递给子组合对象,子组合对象可以读取和更新它。每当缓存的状态被更新时,只有子组合对象被重组,因为它有自己的重组作用域。这意味着我们避免了重新运行CachedUpdate
组合,这将导致覆盖缓存状态。为了以去抖动的方式将缓存状态的更改写回数据库,我们将其转换为流并使用debounce
方法。对于每次更新,我们调用提供的回调将数据写入DB。请注意,我们仍然需要收集流,因为snapshotFlow
是cold流。由于这是一个挂起函数,我们需要正确地确定它的范围,所以当我们的CachedUpdate
可组合(以及包含的文本字段)离开组合时,它将被取消。这可以通过LaunchedEffect来实现。为了确保在流的任何参数(去抖时间和回调)发生变化时创建新的流,我们将这些参数作为key传递给LaunchedEffect
。