https://kotlinlang.org logo
#compose
Title
# compose
l

Landry Norris

02/05/2022, 7:59 PM
In general, will individual state variables be faster than a single data class that holds all the state for a screen? I know that when using individual variables, Compose will only re-render Composables that use the value of the variable that was changed. When using a single data class, is Compose smart enough to only re-render Composables that use the fields that changed, or will it re-render every Composable that uses any field of the single state variable?
I was reading through https://developer.android.com/jetpack/compose/phases#3-phases and saw this quote: "When the state value changes, the recomposer schedules reruns of all the composable functions which read that state value", which makes me think it re-renders every Composable that uses the single variable, without skipping those that read fields that are the same.
a

Adam Powell

02/05/2022, 8:04 PM
Skipping has to do with determining stable equality. To skip, all parameters to a
@Composable fun
must be known to be stable, and their new values must compare
.equals
to their old values.
Your data class might be stable, but if it's part a very deep tree of data classes,
.equals
might take a while
l

Landry Norris

02/05/2022, 8:11 PM
So the equality check is for the parameters to the composable, not the state variable? For example, if I have a LoginState class that holds username, password, and maybe a few more variables, and I change only the email field of the LoginState, can I assume that the password box, for instance, would not have to re-render (assuming the password Composable doesn't use the email field)?
a

Adam Powell

02/05/2022, 8:21 PM
it might help if you write out the code you're thinking of and your expectations for it
compose does not perform any instrumentation of your immutable data classes behind the scenes to have any kind of per-property behavior
l

Landry Norris

02/05/2022, 8:29 PM
Let's say I have two text fields that get their text from variables,
Copy code
Column {
    Text(text1)
    Text(text2)
}
in the case of multiple variables, or
Copy code
Column {
    Text(state.text1)
    Text(state.text2)
}
in the case of a data class. You could have text1 and text2 as two different state variables, where I know that running text1.value = "foo" only re-renders the first text, or you could have a data class ScreenState(text1, text2). In the second case, if I were to run
state.value = state.value.copy(text1 = "foo")
, would this be identical to the first case, causing only the top Text to get re-rendered, or will both get re-rendered due to changing the value of the single state variable?
a

Adam Powell

02/05/2022, 8:35 PM
These will cause identical recomposition behavior, here's why:
Kotlin function calls are pass by value, so any read happens before the function call. That's what is tracked for invalidation.
Copy code
var text1 by remember { mutableStateOf(...) }
var text2 by remember { mutableStateOf(...) }
// ...
Column {
  Text(text1) // call to getValue happens while evaluating parameters to pass
  Text(text2) // call to getValue happens while evaluating parameters to pass
}
So the containing recompose scope of the
Text
call is what is invalidated in both cases
and since
Column
is inline, the containing recompose scope is the containing scope of
Column
both of the snippets you posted rely on
Text
skipping when the string passed to it is the same as the previous recomposition
the scope of invalidation is determined by where the
.value
(or delegate
getValue
) occurs
l

Landry Norris

02/05/2022, 8:44 PM
I see. So both Texts get recomposed, but the second one detects that the value is the same in both cases, so it doesn't re-render, while the first one does in both cases, since the value has changed. Is my understanding correct?
a

Adam Powell

02/05/2022, 8:46 PM
the caller of both
Texts
gets recomposed. Both calls to
Text
happen. The function body skipping behavior added to the
Text
function by the compose compiler plugin compares the parameters of skippable composable functions and determines whether to return early without doing work or not
l

Landry Norris

02/05/2022, 8:48 PM
Is there a way to see the code that Compose generates? I learned the insides of the old View system over time, but Compose is still new to me.
a

Adam Powell

02/05/2022, 8:49 PM
you can decompile the final class files; I don't think the IDE's show kotlin bytecode functionality runs the compose compiler but my memory of this might be stale
you won't find anything about snapshot invalidation in there though, snapshots are entirely a runtime construct and have no relation to the compose compiler plugin
l

Landry Norris

02/05/2022, 8:52 PM
Ok. Thank you.
👍 1