Helios
Helios

Reputation: 71

Remember in Composition is forgotten

I'm at Google CodeLabs: And here is the code:

@Composable
fun WaterCounter(
    modifier: Modifier = Modifier,
) {
    var count by remember { mutableStateOf(0) }

    Column(
        modifier = modifier.padding(16.dp)
    ) {

        if (count > 0) {
            var showTask by remember { mutableStateOf(true) }
            if (showTask) {
                WellnessTaskItem(
                    taskName = "Have you taken your 15 minute walk today?",
                    onClose = { showTask = false })
            }
            Text(
                text = "You've had $count glasses",
                modifier = modifier.padding(16.dp)
            )
        }


        Row(
            modifier = modifier.padding(top = 8.dp)
        ) {
            Button(
                onClick = { count++ },
                enabled = count < 10,
            ) {
                Text(text = "add one")
            }

            Button(
                onClick = { count = 0 },
                modifier = modifier.padding(start = 8.dp)
            ) {
                Text(text = "Clear water count")
            }
        }
    }
}

I can't get this phrase: "Press the Clear water count button to reset count to 0 and cause a recomposition. Text showing count, and all code related to WellnessTaskItem, are not invoked and leave the Composition. showTask is forgotten because the code location where remember showTask is called was not invoked."

Please help me understand why show task is forgotten? What does it mean "the code was not invoked" and "it leaves the composition"?

Upvotes: 3

Views: 501

Answers (1)

Thracian
Thracian

Reputation: 67443

Conditional code blocks enter composition when condition is true and leave composition when condition cease to be true. When count is zero that block is removed and next time it enters composition remember stores showTask with true value.

For instance the code block below

@Composable fun App() {
 val result = getData()
 if (result == null) {
   Loading(...)
 } else {
   Header(result)
   Body(result)
 }
}

these blocks also enter composition based on result is null or not. Entering Composition means it's a node that starts execution and when a State that is read in this block is updated recomposition happens which means same block is updated with new value.

enter image description here

when result is null Loading Composable enters composition, let's say it has a progress bar with value when it's increased Loading Composable is recomposed.

when a result comes Loading Composable exits composition

   Header(result)
   Body(result)

enters composition enter image description here

Also, Composable blocks are not necessarily need to be UI related either. You can create a Composable block for with LaunchedEffect to display a SnackBar for instance.

if (count > 0 && count <5) {
    // `LaunchedEffect` will cancel and re-launch if
    // `scaffoldState.snackbarHostState` changes
    LaunchedEffect(scaffoldState.snackbarHostState) {
        // Show snackbar using a coroutine, when the coroutine is cancelled the
        // snackbar will automatically dismiss. This coroutine will cancel whenever
        // if statement is false, and only start when statement is true
        // (due to the above if-check), or if `scaffoldState.snackbarHostState` changes.
        scaffoldState.snackbarHostState.showSnackbar("count $count")
    }
}

This block will enter composition when count is bigger than 0 and stay in composition while count is less than 5 but since it's LaunchedEffect it will trigger once but if count reaches 5 faster than Snackbar duration Snackbar gets canceled because block leaves composition.

You can check when a Composable enters and exits composition with DisposableEffect

And you can check this article for deeper analysis.

https://medium.com/androiddevelopers/under-the-hood-of-jetpack-compose-part-2-of-2-37b2c20c6cdd

Upvotes: 3

Related Questions