Vova
Vova

Reputation: 15

Infinite LaunchedEffect in Jetpack Compose

I'm new to Jetpack Compose and my problem is that I'm trying to get data (JSON) from the server after navigation and I'm getting an infinite loop with LaunchedEffect.

My UI code :

@Composable
fun ListView(
    viewModelItems: ItemsViewModel = viewModel(),
) {
    var stateData = remember {
        mutableStateOf(0)
    }

    if(stateData.value == 0) {
        LaunchedEffect(Unit) {
            viewModelItems.getList()
            viewModelItems.state.collectLatest {
                stateData.value = it.state
            }
        }
    }


    when(stateData.value){
        ...
    }

I think this is a problem with stateData because it is not updated after recomposition.

My ViewModel code :

class ItemsViewModel : ViewModel(){

    private var listState = MutableStateFlow<ListState>(Loading(null, 0))

    var state =  listState.asStateFlow()

    fun getList(){
        val res = SkovService.getInstance().getList()

        res.enqueue(object : Callback<Items?>{
            override fun onResponse(call: Call<Items?>, response: Response<Items?>) {
                listState.value = Success(response.body(), 1)
            }

            override fun onFailure(call: Call<Items?>, t: Throwable) {
                listState.value = Error(null, -1)
                call.cancel()
            }

        })
    }
}

ListState is a sealed class .

I've been trying to solve this problem for 1 week =D, if you know how to solve it, please help me.

Upvotes: 1

Views: 532

Answers (1)

cesonha
cesonha

Reputation: 1205

As @commonsware said, you can avoid using the LaunchedEffect and use the collectAsState with the ListState object itself. Then there will be no need for the -1, 0 and 1 within the ListState class and you can just use the type of the ListState within the when clause

@Composable
fun ListView(
    viewModelItems: ItemsViewModel = viewModel(),
) {
    val stateData by viewModelItems.state.collectAsState()

    when (stateData.value) {
       is Loading -> { // render loading components }
       is Error -> { // render error components }
       is Success -> { // use state value received from response.body() to render the list }
}

And you would need to call viewModelItems.getList() outside of this @Composable, or you can call it in the init block of the ItemsViewModel

Upvotes: 1

Related Questions