4

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.

MobileEvangelist
  • 2,583
  • 1
  • 25
  • 36
Jono
  • 17,341
  • 48
  • 135
  • 217

1 Answers1

2

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.

RobertoAllende
  • 8,744
  • 4
  • 30
  • 49