Jetpack Compose MutableState 未在屏幕上更新
Jetpack Compose MutableState not updating on the screen
我想显示来自 Firebase 实时数据库的电影列表,但我没有从 ViewModel 中获取列表。
当我更新存储库中的 apiResource.data
时,它不会更新屏幕上的 mutableList。
屏幕:
val movies = viewModel.data.value.data?.toMutableList()
if (viewModel.data.value.loading == true) {
CircularProgressIndicator()
} else {
movies?.forEach {
Log.d("Movies", it.name) // I'm not getting the movie list on the screen
}
}
视图模型:
val data: MutableState<ApiResource<List<Movie>, Boolean, Exception>> = mutableStateOf(
ApiResource(null, true, Exception(""))
)
private fun getMovies() {
viewModelScope.launch {
data.value.loading = true
data.value = repository.getAllMovies()
if (data.value.data.toString() != "[]") {
data.value.loading = false
}
}
}
存储库:
class Repository {
private val apiResource = ApiResource<List<Movie>,Boolean, Exception>()
suspend fun getAllMovies(): ApiResource<List<Movie>, Boolean, java.lang.Exception> {
try {
apiResource.loading = true
var movies: List<Movie>
Firebase.database.reference.child("movies").addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
movies = dataSnapshot.children.map { snapshot ->
snapshot.getValue(Movie::class.java)!!
}
apiResource.data = movies // set data in apiResource
if (apiResource.data.toString() != "[]") {
apiResource.loading = false
}
}
override fun onCancelled(error: DatabaseError) {}
})
} catch (exception: java.lang.Exception) {
apiResource.e = exception
}
return apiResource
}
}
Compose 无法跟踪可变状态包含的对象的更改。它只能跟踪对可变状态值的更新。
可能的解决方案:
通过创建具有更新属性的对象副本来更新您的可变状态。数据 class 在这种情况下非常方便:
data class ApiResource(val isLoading: Boolean = false)
class VM : ViewModel() {
var apiResource by mutableStateOf(ApiResource())
fun update() {
apiResource = apiResource.copy(isLoading = true)
}
}
制作 ApiResource
可变状态的必要属性。
class ApiResource {
var isLoading by mutableStateOf(false)
}
class VM: ViewModel() {
val apiResource = ApiResource()
fun update() {
apiResource.isLoading = true
}
}
您的代码的第二个问题是,您将协程与异步代码混为一谈。 addValueEventListener
不会等到 onDataChange
被调用,所以 getAllMovies
总是 returns 一个空对象。
要让协程等待异步调用完成,您需要使用 suspendCoroutine
.
此外,如果结果发生变化,提供给 addValueEventListener
的侦听器可以多次调用。您当前的代码不希望这样,我建议您改用 addListenerForSingleValueEvent
。
val movies = suspendCoroutine<List<Movie>> { continuation ->
Firebase.database.reference.child("movies").addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
continuation(
dataSnapshot.children.map { snapshot ->
snapshot.getValue(Movie::class.java)!!
}
)
}
override fun onCancelled(error: DatabaseError) {}
})
}
我想显示来自 Firebase 实时数据库的电影列表,但我没有从 ViewModel 中获取列表。
当我更新存储库中的 apiResource.data
时,它不会更新屏幕上的 mutableList。
屏幕:
val movies = viewModel.data.value.data?.toMutableList()
if (viewModel.data.value.loading == true) {
CircularProgressIndicator()
} else {
movies?.forEach {
Log.d("Movies", it.name) // I'm not getting the movie list on the screen
}
}
视图模型:
val data: MutableState<ApiResource<List<Movie>, Boolean, Exception>> = mutableStateOf(
ApiResource(null, true, Exception(""))
)
private fun getMovies() {
viewModelScope.launch {
data.value.loading = true
data.value = repository.getAllMovies()
if (data.value.data.toString() != "[]") {
data.value.loading = false
}
}
}
存储库:
class Repository {
private val apiResource = ApiResource<List<Movie>,Boolean, Exception>()
suspend fun getAllMovies(): ApiResource<List<Movie>, Boolean, java.lang.Exception> {
try {
apiResource.loading = true
var movies: List<Movie>
Firebase.database.reference.child("movies").addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
movies = dataSnapshot.children.map { snapshot ->
snapshot.getValue(Movie::class.java)!!
}
apiResource.data = movies // set data in apiResource
if (apiResource.data.toString() != "[]") {
apiResource.loading = false
}
}
override fun onCancelled(error: DatabaseError) {}
})
} catch (exception: java.lang.Exception) {
apiResource.e = exception
}
return apiResource
}
}
Compose 无法跟踪可变状态包含的对象的更改。它只能跟踪对可变状态值的更新。
可能的解决方案:
通过创建具有更新属性的对象副本来更新您的可变状态。数据 class 在这种情况下非常方便:
data class ApiResource(val isLoading: Boolean = false) class VM : ViewModel() { var apiResource by mutableStateOf(ApiResource()) fun update() { apiResource = apiResource.copy(isLoading = true) } }
制作
ApiResource
可变状态的必要属性。class ApiResource { var isLoading by mutableStateOf(false) } class VM: ViewModel() { val apiResource = ApiResource() fun update() { apiResource.isLoading = true } }
您的代码的第二个问题是,您将协程与异步代码混为一谈。 addValueEventListener
不会等到 onDataChange
被调用,所以 getAllMovies
总是 returns 一个空对象。
要让协程等待异步调用完成,您需要使用 suspendCoroutine
.
此外,如果结果发生变化,提供给 addValueEventListener
的侦听器可以多次调用。您当前的代码不希望这样,我建议您改用 addListenerForSingleValueEvent
。
val movies = suspendCoroutine<List<Movie>> { continuation ->
Firebase.database.reference.child("movies").addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
continuation(
dataSnapshot.children.map { snapshot ->
snapshot.getValue(Movie::class.java)!!
}
)
}
override fun onCancelled(error: DatabaseError) {}
})
}