Reputation: 578
There is a screen level composable under an activity. When user is navigated to that screen, I'm using a LaunchedEffect(Unit) {}
to call view-model function which does some work. Now, this works fine until the device rotates or any configuration change occurs. After rotation or so, LaunchedEffect(Unit) {}
is executed again which is not desirable.
I know I can use a flag in the view-model or in the composable function itself to prevent doing the work again. But, writing here to find if there is any ideal solution to this problem as this seems to be a common problem in Jetpack Compose.
PS: The function needs to be in the screen level view-model only and not at activity-level. Also, cannot use init
of view-model as the function which the screen-level composable is calling needs some data as it's parameters.
Upvotes: 5
Views: 2719
Reputation: 1307
From your compose view, you can do something like that,
LaunchedEffect(viewModel) {
viewModel.whatever()
}
It will be triggered only when viewModel change, so it should be executed only once. I hope it helps.
Upvotes: 0
Reputation: 5443
You can implement some basic caching in your ViewModel
. Here is a hypothetical example of where the ViewModel
will return cached data to subscribers during configuration changes.
class MainViewModel : ViewModel() {
sealed interface MainViewState {
data object Content : MainViewState
data object Loading : MainViewState
data class Error(val error: Throwable) : MainViewState
}
private val _viewState: MutableStateFlow<MainViewState> =
MutableStateFlow(MainViewState.Loading)
val viewState = _viewState.asStateFlow()
init {
loadData()
}
private fun loadData() = viewModelScope.launch {
// We have cached data, do nothing, StateFlow will re-emit latest state to new subscribers
if (_viewState.value == MainViewState.Content) {
return@launch
}
_viewState.update {
MainViewState.Loading
}
// Load some data and return MainViewState.Content or MainViewState.Error
}
}
Upvotes: 0
Reputation: 1501
LaunchedEffect
will run every time it recomposes. You can use rememberSaveable
for this and add a flag to not run the code again. Actually, it would be more accurate to do this in a viewModel
, but since you specifically want it this way, you can look at this example.
var workCompleted by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(Unit) {
if (!workCompleted) {
// your work
workCompleted = true
}
}
Upvotes: 0