Reputation: 18128
I have question regarding using observeAsState() to automatically populate a composable list view.
My composable looks like this
@Composable
fun getTopMovies() {
val topMovies by movieListViewModel.getTopMovies().observeAsState()
when (topMovies?.status) {
Status.Error -> Text("error")
Status.Loading -> {
Log.d("JJJ", "Loading ")
Text(text = "Loading")
}
Status.Success -> createMovieItemView(topMovies?.data?.results.orEmpty())
}
}
This uses MVVM to do a network call to fetch some list of data and return it it back as a livedata.
The issue i am having is that it seems stuck on a infinit loop. If i dont use observeeAsState and just use the normal none composable way ie:
movieListViewModel.getTopMovies().observe(viewLifecycleOwner, Observer { ...}
it works as expected and executes and ends once a error or a success is returned from the repository/domain layer.
This is the createMovieItemView below:
@Composable
private fun createMovieItemView(movieList: List<MovieItem>) {
LazyColumnFor(items = movieList, itemContent = { movieItem ->
MovieListItem(MovieItemData(movieItem.posterPath.orEmpty(),
movieItem.title.orEmpty(),
movieItem.releaseDate.orEmpty(),
"some genra", ""), picasso)
})
}
to automatically populate a composable list view.
My composable looks like this
@Composable
fun getTopMovies() {
val topMovies by movieListViewModel.getTopMovies().observeAsState()
when (topMovies?.status) {
Status.Error -> Text("error")
Status.Loading -> {
Log.d("JJJ", "Loading ")
Text(text = "Loading")
}
Status.Success -> createMovieItemView(topMovies?.data?.results.orEmpty())
}
}
This uses MVVM, to do a network call to fetch some list of data and then returns a livedata.
Issue seems to be, it stuck in a infinit loop. If i dont use observeeAsState and just use the normal none composable way i.e. movieListViewModel.getTopMovies().observe(viewLifecycleOwner, Observer { it works as expected, executes and ends once an error or a success is returned from the repository/domain layer.
Upvotes: 4
Views: 7126
Reputation: 9548
I had a similar problem and I solved by wrapping getTopMovies
inside a LaunchedEffect
block and I end up with something like this:
@Composable
fun MoviesScreen(onTimeout: () -> Unit) {
val topMovies by viewModel.topMovies.observeAsState()
LaunchedEffect(true) {
movieListViewModel.getTopMovies().observeAsState()
}
}
Whereas the viewModel would be something like:
class MoviesViewModel(): ViewModel {
var _topMovies: MutableLiveData<List<Movies>> = mutableLiveDataOf(listOf())
val topMovies: LiveData<List<Movies>>
fun getTopMovies(){
_topMovies.postValue(repository.getTopMovies())
}
}
Note this is pseudo-code to describe the solution. Which is inspired on rememberUpdatedState extample
If getTopMovies
uses co-routines, it has side-effects, therefore LaunchedEffect
is needed because Composables should be side-effect free as it is described at Side-effects in Compose.
Upvotes: 2