Jono
Jono

Reputation: 18128

Android jetpack compose observing a livedata

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

Answers (1)

RobertoAllende
RobertoAllende

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

Related Questions