Search code examples
androiddagger-2android-jetpack-composeandroid-jetpackdagger-hilt

Jetpack compose data store keeps recomposing screen


I'm migrating from Shared preference to data store using jetpack compose. everything works fine (data is saved and can be retreated successfully). However, whenever a Data is retrieved, the composable keeps on recomposing endlessly. I'm using MVVM architecture and below is how I have implemented data store.

Below is declared in my AppModule.kt

App module in SingletonComponent

@Provides
@Singleton
fun provideUserPreferenceRepository(@ApplicationContext context: Context):
        UserPreferencesRepository = UserPreferencesRepositoryImpl(context)

Then here's my ViewModel:

@HiltViewModel
class StoredUserViewModel @Inject constructor(
private val _getUserDataUseCase: GetUserDataUseCase
): ViewModel() {

private val _state = mutableStateOf(UserState())
val state: State<UserState> = _state

fun getUser(){
    _getUserDataUseCase().onEach { result ->
        val name = result.name
        val token = result.api_token
        _state.value = UserState(user = UserPreferences(name, agentCode, token, balance))
    }.launchIn(viewModelScope)
 }}

Finally, Here's my Repository Implementation:

class UserPreferencesRepositoryImpl @Inject constructor(
private val context: Context
): UserPreferencesRepository {

private val Context.dataStore by preferencesDataStore(name = "user_preferences")
}
private object Keys {
    val fullName = stringPreferencesKey("full_name")
    val api_token = stringPreferencesKey("api_token")
}

private inline val Preferences.fullName get() = this[Keys.fullName] ?: ""
private inline val Preferences.apiToken get() = this[Keys.api_token] ?: ""

override val userPreferences: Flow<UserPreferences> = context.dataStore.data.catch{
//        throws an IOException when an error is encountered when reading data
    if (it is IOException) {
        emit(emptyPreferences())
    } else {
        throw it
    }
}.map { preferences ->
    UserPreferences(name = preferences.fullName, api_token = preferences.apiToken)
}.distinctUntilChanged()

I don't know what causes the composable to recompose. Below Is the composable:

@Composable
fun LoginScreen(
navController: NavController,
userViewModel: StoredUserViewModel = hiltViewModel()
) {

Log.v("LOGIN_SCREEN", "CALLED!")
userViewModel.getUser()
}

If anyone can tell me where I've done wrong please enlighten me. I have tried to change the implementation in AppModule for UserPreferencesRepository but no luck.

Below is UseState.kt which is just a data class

data class UserState(
val user: UserPreferences? = null
)

Below is UserPreferences.kt

data class UserPreferences(val name: String, val api_token: String)

Solution

  • I also faced such problem. The solution was became to navigate with LauchedEffect in composable.

    before:

    if (hasFlight) {
        navController.navigate(Screen.StartMovingScreen.route)
    }
    

    after:

    if (hasFlight) {
        LaunchedEffect(Unit) {
            navController.navigate(Screen.StartMovingScreen.route)
        }
    }