Hi there, quick question. What's the best way to p...
# compose-desktop
m
Hi there, quick question. What's the best way to perform a async call in a Composable ? I used to call my repositories from ViewModels but I don't know the appropriate way to do so with Compose Desktop. Here is a quick example:
Copy code
@Composable
fun RoomsScreen(
    onRoomClick: (Int) -> Unit,
    onBack: () -> Unit
) {
    val rooms = remember { mutableStateOf(emptyList<WSRoom>()) }
    rooms.value = runBlocking { RoomRepository.getAllRooms() }

    ...
}
Here I've noticed that the RoomRepository.getAllRooms gets called every recomposition. What are your recommandation to avoid that ?
h
I would use a view model with its own scope. But alternative you could use LaunchedEffect
m
It would all become much cleaner and simpler if you would change your RoomRepository to export a StateFlow which you could then observe in your composable function.
👍 1
h
or a normal flow with remember
m
Ah awesome, I'll check that out. Thank you guys 🙂
a
And you should avoid
runBlocking
at any situation.
m
Yes, but you won’t need runBlocking anymore anyway if you use a StateFlow.
h
and you should not update a state during recomposition
1
m
@Michael Paus I've experimented with StateFlow but I'm not sure to get it
Copy code
class RoomRepository {
    companion object {
        fun getAllRooms(): StateFlow<List<WSRoom>> {
            val rooms = asyncOperation() // There is more to it but I simplified for this example
            return MutableStateFlow(rooms)
        }
    }
}

@Composable
fun RoomsScreen(
    onRoomClick: (Int) -> Unit,
    onBack: () -> Unit
) {
    val rooms = RoomRepository.getAllRooms().collectAsState()

    ...
}
I still seems to get the same side effect. Is there a way not to call getAllRooms multiple time or am I supposed to pass the rooms as a paramater ? I also checked if I could add this logic in a ViewModel but they don't seem to be available for desktop
m
You are just doing the same mistake again. Just hidden inside your RoomRepository now. I just don’t have a good tutorial at hand to show you how to use StateFlow.
m
No worries, thank you for the help 🙂 I've tried a different approach by storing the StateFlow in the repository, it works but I'm not sure that's an appropriate approach.
Copy code
class RoomRepository {
    companion object {
        private var _rooms = MutableStateFlow(getAllRooms())
        val rooms: StateFlow<List<WSRoom>> = _rooms

        private fun getAllRooms(): List<WSRoom> {
            val events = EventRepository.getCurrentEvents()
            val rooms = mutableListOf<WSRoom>()
            events.forEach { event ->
                rooms.addAll(getEventRooms(event.id))
            }
            return rooms
        }
    }
}

@Composable
fun RoomsScreen(
    onRoomClick: (Int) -> Unit,
    onBack: () -> Unit
) {
    val rooms by RoomRepository.rooms.collectAsState()

    ...
}
m
In your RoomsScreen you have to substitute getAllRooms by rooms.
m
Oops, you're right, I actually did but forgot to update the snippet
h
This still blocks:
getAllRooms
is blocking.
Copy code
class RoomRepository {
    companion object {
    val  rooms: Flow<List<WSRoom>> get() = flow {
         emit(EventRepository.getCurrentEvents())
        }
    }
}

@Composable
fun RoomsScreen(
    onRoomClick: (Int) -> Unit,
    onBack: () -> Unit
) {
    val rooms by remember { RoomRepository.rooms.collectAsState(emptyList()) }

    ...
}