NullPointerException
NullPointerException

Reputation: 37731

Infinite loop of recomposition if I add an if into a composable

A composable with a navhost is recomposing in an infinite loop if I add an if condition to navigate to a screen if a int parameter representing the current section has been changed. The condition has not changed, the parameter is always 1 in each recomposition, but the composable is being recomposed constantly if I add that if. If i remove it, it only recomposes one time.

The reason I need that if is because an external event (not in this composable or their childrens) can change the current screen and is represented by that variable called currentSectionId which is being observed in the parent composable from a stateflow stored in another class.

Anyone can explain me that infinite loop?

@Composable
fun ApplicationNavHost(
    currentSectionId: Int,
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController()
) {
    val sections = SectionHolder.sections
    val startSection = SectionHolder.startSection

    NavHost(
        navController = navController,
        startDestination = startSection.toString(),
        modifier = modifier
    ) {
        for ((i, section) in sections.withIndex()) {
            Log.d("XXXX", "added route: $i")
            composable(route = i.toString()) {
                SectionComposableFactory(
                    section = section,
                )
            }
        }
    }

    if (currentSectionId != startSection.toInt()){
        navController.navigate(currentSectionId.toString())
        Log.d("XXXX", "new section: $currentSectionId")
    }
}

Upvotes: 1

Views: 106

Answers (1)

hasan.z
hasan.z

Reputation: 349

The infinite recomposition loop you’re encountering is most likely due to the navController.navigate() call within the if condition.

  • When the condition is true, you’re calling navController.navigate().
  • This causes a navigation event, which might trigger a recomposition because the NavHost observes changes.
  • During the recomposition, your if block runs again with the same condition result and calls navController.navigate() again, causing a loop. Even though currentSectionId remains the same (1 in this case), each recomposition is re-triggering the navigate() call, resulting in the infinite loop.

Solution:

To prevent this loop, use LaunchedEffect that is a more idiomatic way to handle side effects in Jetpack Compose.

LaunchedEffect(currentSectionId) {
    if (currentSectionId != startSection.toInt()) {
        navController.navigate(currentSectionId.toString())
        Log.d("XXXX", "new section: $currentSectionId")
    }
}

LaunchedEffect takes currentSectionId as a key. When currentSectionId changes, the block inside LaunchedEffect will be executed. This ensures that navigation occurs only when the currentSectionId changes, and not on every recomposition.

I highly recommend checking out this Android document on Side-effects in Compose for a deeper understanding.

Upvotes: 0

Related Questions