如何使用 Jetpack Compose 将异步操作的响应传播到视图?
How to propagate the response of an async operation to the view using Jetpack Compose?
我把这个封起来了class:
sealed class Resource<out T> {
object Loading: Resource<Nothing>()
data class Success<out T>(val data: T): Resource<T>()
data class Failure(val message: String): Resource<Nothing>()
}
在存储库 class 中,我有这个功能可以从 API:
中删除一个项目
override suspend fun deleteItem(id: String) = flow {
try {
emit(Resource.Loading)
emit(Resource.Success(itemsRef.document(id).delete().await()))
} catch (e: Exception) {
emit(Resource.Failure(e.message))
}
}
删除操作的结果是Void?
。现在,在 ViewModel class 中我声明:
val state = mutableStateOf<Resource<Void?>>(Success(null))
并在删除完成后更新它:
fun deleteItem(id: String) {
viewModelScope.launch {
repo.deleteItem(id).collect { response ->
state.value = response
}
}
}
我创建了一张卡片并在 onClick 中添加了:
IconButton(
onClick = viewModel.deleteItem(id),
)
这实际上从数据库中正确删除了该项目。但是我无法跟踪操作的结果。我尝试使用:
when(val res = viewModel.state.value) {
is Resource.Loading -> Log.d(TAG, "Loading")
is Resource.Success -> Log.d(TAG, "Success")
is Resource.Failure -> Log.d(TAG, "Failure")
}
但只有案例Loading被触发。根本没有 success/failure。这里有什么问题?因为它真的像一个同步操作。
尝试在可组合函数中收集:
val state = viewModel.state.collectAsState()
那么你可以这样做: when (val res = viewModel.state.value){...}
.
但是我对存储库中返回 flow
的 deleteItem
持怀疑态度。你真的需要这样的东西吗?您总是可以 map
viewModel
中的内容。
我已经在没有存储库的情况下测试了你的方法,组成部分看起来完全没问题:
var i = 0
@Composable
fun TestScreen(viewModel: TestViewModel = viewModel()) {
val state by viewModel.state
Text(
when (val stateSmartCast = state) {
is Resource.Failure -> "Failure ${stateSmartCast.message}"
Resource.Loading -> "Loading"
is Resource.Success -> "Success ${stateSmartCast.data}"
}
)
Button(onClick = {
viewModel.deleteItem(++i)
}) {
}
}
class TestViewModel : ViewModel() {
val state = mutableStateOf<Resource<Int>>(Resource.Success(i))
fun deleteItem(id: Int) {
viewModelScope.launch {
deleteItemInternal(id).collect { response ->
state.value = response
}
}
}
suspend fun deleteItemInternal(id: Int) = flow {
try {
emit(Resource.Loading)
delay(1000)
if (id % 3 == 0) {
throw IllegalStateException("error on third")
}
emit(Resource.Success(id))
} catch (e: Exception) {
emit(Resource.Failure(e.message ?: e.toString()))
}
}
}
所以问题看起来像在这一行 itemsRef.document(id).delete().await())
中,或者在您与存储库的连接中。
我把这个封起来了class:
sealed class Resource<out T> {
object Loading: Resource<Nothing>()
data class Success<out T>(val data: T): Resource<T>()
data class Failure(val message: String): Resource<Nothing>()
}
在存储库 class 中,我有这个功能可以从 API:
中删除一个项目override suspend fun deleteItem(id: String) = flow {
try {
emit(Resource.Loading)
emit(Resource.Success(itemsRef.document(id).delete().await()))
} catch (e: Exception) {
emit(Resource.Failure(e.message))
}
}
删除操作的结果是Void?
。现在,在 ViewModel class 中我声明:
val state = mutableStateOf<Resource<Void?>>(Success(null))
并在删除完成后更新它:
fun deleteItem(id: String) {
viewModelScope.launch {
repo.deleteItem(id).collect { response ->
state.value = response
}
}
}
我创建了一张卡片并在 onClick 中添加了:
IconButton(
onClick = viewModel.deleteItem(id),
)
这实际上从数据库中正确删除了该项目。但是我无法跟踪操作的结果。我尝试使用:
when(val res = viewModel.state.value) {
is Resource.Loading -> Log.d(TAG, "Loading")
is Resource.Success -> Log.d(TAG, "Success")
is Resource.Failure -> Log.d(TAG, "Failure")
}
但只有案例Loading被触发。根本没有 success/failure。这里有什么问题?因为它真的像一个同步操作。
尝试在可组合函数中收集:
val state = viewModel.state.collectAsState()
那么你可以这样做: when (val res = viewModel.state.value){...}
.
但是我对存储库中返回 flow
的 deleteItem
持怀疑态度。你真的需要这样的东西吗?您总是可以 map
viewModel
中的内容。
我已经在没有存储库的情况下测试了你的方法,组成部分看起来完全没问题:
var i = 0
@Composable
fun TestScreen(viewModel: TestViewModel = viewModel()) {
val state by viewModel.state
Text(
when (val stateSmartCast = state) {
is Resource.Failure -> "Failure ${stateSmartCast.message}"
Resource.Loading -> "Loading"
is Resource.Success -> "Success ${stateSmartCast.data}"
}
)
Button(onClick = {
viewModel.deleteItem(++i)
}) {
}
}
class TestViewModel : ViewModel() {
val state = mutableStateOf<Resource<Int>>(Resource.Success(i))
fun deleteItem(id: Int) {
viewModelScope.launch {
deleteItemInternal(id).collect { response ->
state.value = response
}
}
}
suspend fun deleteItemInternal(id: Int) = flow {
try {
emit(Resource.Loading)
delay(1000)
if (id % 3 == 0) {
throw IllegalStateException("error on third")
}
emit(Resource.Success(id))
} catch (e: Exception) {
emit(Resource.Failure(e.message ?: e.toString()))
}
}
}
所以问题看起来像在这一行 itemsRef.document(id).delete().await())
中,或者在您与存储库的连接中。