Hey guys, could someone explain this to me please?...
# compose-android
m
Hey guys, could someone explain this to me please? Consider this composable function:
Copy code
Column {
    var counter by mutableIntStateOf(0)
    Button(onClick = { counter++ }) {
       Text(text = "Counter $counter")
    }
                    
    // Text(text = "Counter $counter")
}
If I comment the Text the counter will be increased whenever I click on the button. But when I uncomment it the counter is always 0. I think that's because the Text composable is in the main scope of the parent composable function whenever it changes the whole function is recomposed and the counter is reset to 0. Am I thinking correctly or missing something here?
s
Yeah if nobody is reading the state, there's no invalidation, so no recomposition. With that said, remember to remember your mutableStates
Oh, but the Button, is that not inline?
đźš« 2
a
IDE should highlight it as an error in any case
s
If it's inline, it would invalidate the parent scope too anyway. In any case though, don't rely on such things, just
remember
it as you should.
a
Uh what? I swear this used to be an error earlier (red squiggly line). Did something change or am I remembering (heh) wrong?
s
Maybe the lint does not detect unremembered
mutable{{Int/Float/..}}StateOf
while it does
mutableStateOf
calls? In any case, it’s not a compilation error to do this wrong, you just don’t get the right behavior
s
Yeah, seems like an oversight, feel free to file a bug if that's the case 🙂
m
@Stylianos Gakis The IDE gives a warning for unremembered mutable state for me.
I couldn't understand the reason of this yet. If someone can explain it to me I would be appreciate it.
s
The reason why it needs to be remembered? If that is what you are confused about, take a look at these https://dev.to/zachklipp/remember-mutablestateof-a-cheat-sheet-10ma https://developer.android.com/jetpack/compose/state#state-in-composables
m
I know that by remembering it the problem will fixed. But thing that confuses me is when I uncomment the outer
Text
Composable, the counter won't increase any more
s
Well, for that question, read this https://dev.to/zachklipp/scoped-recomposition-jetpack-compose-what-happens-when-state-changes-l78 to understand when a composable recomposes, and what does it mean for there to be multiple “recomposition scopes” in your composable, which may mean that a smaller part of your composable may only recompose if the state read is happening only inside that inner smaller recomposition scope
🆗 1
👍 1
In your case, you got 2 recomposition scopes, the big one ecompassing the entire Column, and the smaller one, which is the lambda of the Button
Copy code
Column {                                                    < -
    var counter by mutableIntStateOf(0)                       |
    Button(onClick = { counter++ }) {     < -                 |
       Text(text = "Counter $counter")      | inner scope #2  | outer scope, #1
    }                                     < -                 |
                                                              |
    // Text(text = "Counter $counter")                        |
}                                                           < -
You got the mutableState defined in scope #1. That does not mean much by itself, for compose to determine when something will recompose, it needs to be reading the mutable state. In the commented out situation: The only place where you read the state is inside scope #2. That means that when the mutableState changes, scope #2 is listening on those changes, since you are using it there, and when it changes it recomposes the Button lambda. In the situation where you do not have the other text commented out: Then you are reading
counter
both in scope #2 and in scope #1. That means when mutableState changes, both scope #1 and scope #2 see the change, and they decide to recompose themselves. When that happens however now, since we are inside scope #1 and it is being recomposed, the line
var counter by mutableIntStateOf(0)
is run again, so the mutableState is set to
0
again. And that’s it, this is what you are observing here. Interestingly! If
Button
was an
inline
function, then the scope #2 would simply not exist, it’d all be one recomposition scope. Which would mean you would see the second behavior regardless on if you have the other
Text
which is reading the
counter
mutableState or not
m
Thank you a lot @Stylianos Gakis. I got it. So as I understood I wrapped the outer
Text
in another
Button
because Button is not inlined, and the counter increases whenever I click on each button. And that makes sense. Thank u again for great explanation. 👍
s
Yes, that would work, but this is not the real solution here. As mentioned before, the real solution is to remember your
mutableState
defined inside a @Composable function. That’s the verdict you should get out of this discussion.
m
Of course. I knew that. Actually I was testing the recomposition scopes and wanted to understand them better.
👌 2