3

I'm implementing registration in my application and, after filling in the respective fields, I click on a button that will make a registration request to the API. Meanwhile, I place a Loading View and when I receive the successful response, I execute the navigation to the OnBoarding screen. The issue is that the navController is always running the navigation and doing the navigation and popUp several times, when it should only do it once. I always get this warning on logs: Ignoring popBackStack to destination 29021787 as it was not found on the current back stack and I am not able to do any click or focus in the OnBoardingScreen.

My code:

val uiState by registerViewModel.uiState.collectAsState()

when (uiState) {
        is BaseViewState.Data -> {
            navController.navigate(NavigationItem.OnBoarding.route) {
                popUpTo(NavigationItem.Register.route) {
                    inclusive = true
                }
            }
        }
        is BaseViewState.Loading -> LoadingView()
        is BaseViewState.Error -> BannerView()
        else -> {}
    }

On button click I call the viewModel like this:

registerViewModel.onTriggerEvent(
                    RegisterEvent.CreateUser(
                        usernameInputState.value.text,
                        emailInputState.value.text,
                        passwordInputState.value.text
                    )
                )

And, in ViewModel, I do my request like this:

override fun onTriggerEvent(eventType: RegisterEvent) {
        when (eventType) {
            is RegisterEvent.CreateUser -> createUser(eventType.username, eventType.email, eventType.password)
        }
    }

    private fun createUser(username: String, email: String, password: String) = safeLaunch {
        setState(BaseViewState.Loading)
        execute(createUser(CreateUser.Params(username, email, password))) {
            setState(BaseViewState.Data(RegisterViewState(it)))
        }
    }

I guess it should be caused by recomposition, because I put a breakpoint on first when scenario and it stops here multiple times, but only one on ViewModel. How can I fix this?

z.g.y
  • 5,512
  • 4
  • 10
  • 36
R0ck
  • 409
  • 1
  • 15

1 Answers1

3

This issue is here

is BaseViewState.Data -> {
        navController.navigate(NavigationItem.OnBoarding.route) {
            popUpTo(NavigationItem.Register.route) {
                 inclusive = true
            }
        }
}

Every time you call navController.navigate NavHost will keep on passing through this block, executing an endless loop.

I suggest having the navigate call from a LaunchedEffect with a key (like this),

LaunchedEffect(key1 = "some key") {
     navController.navigate(…)
}

or creating a separate structure namely "Events" where they are emitted as SharedFlow and observed via a Unit keyed LaunchedEffect

LaunchedEffect(Unit) {
        viewModel.event.collectLatest {
            when (it) {
                is UiEvent.Navigate -> {
                    navController.navigate(…)
                }
            }
        }
    }
z.g.y
  • 5,512
  • 4
  • 10
  • 36
  • 1
    Which is the best way? Adding the LaunchedEffect with key worked, thank you so much! – R0ck Nov 28 '22 at 14:44
  • Thank you and youre welcome. It depends though, personally I don't want to manage the `Key`, I'm confident and have a peace of mind knowing that the effect (LaunchedEffect) is only guaranteed to be executed only once since the key is `Unit`, and I have a data structure hierarchy where I consider them as "one-time-event" only – z.g.y Nov 28 '22 at 14:46
  • 1
    That sounds reasonable. I think I will follow the same approach, thank you. – R0ck Nov 28 '22 at 14:47
  • It's not working, I tried with LaunchedEffect still its navigate to multiple times – Tippu Fisal Sheriff Jan 05 '23 at 14:54
  • Nobody can guess how your code is structured, it would be better to create an S.O post and including a reproducible code to see why its still firing on a LaunchedEffect – z.g.y Jan 06 '23 at 01:39