Klaas Kabini
02/20/2020, 7:12 PM@Model
annotation will make the count property inside the LabelState
data class observable which in turn gives it the ability to trigger recompositions on subsequent mutations.
@Model
data class LabelState(var count:Int)
Sample 1:
Composable functions are positionally memoized meaning the inputs to composable functions, and their corresponding outcome resulting from composable functions calls are cached to avoid unneccessary recompositions when a composable function is called with the same inputs multiple times. In case where a composable function is called with the same inputs multiple times, compose runtime will check against the Slot table whether the same inputs have corresponding outputs that were previously cached. If yes, then the compose runtime will retrieve the previously cached results otherwise insert new inputs and their corresponding results to the Slot Table.
Given that understanding, it means that the LabelState
passed as input parameter to a composable in the following code snippet will be positionally memoized despite that is is not wrapped inside remember
composable. Did I understood this correctly? If yes, then is this only applicable to situations where the state is hoisted? - in other words passed down the composable hierarchy from the owner to called composable functions?
@Composable
fun LabelThatPassStateAsParameter(labelState: LabelState){
Text(text = "$labelState.count",
style = MaterialTheme.typography().body1)
}
Sample 2
Alternatively, when the state is not passed as input to composable function, it can be obtained by a keeping a local variable that references the state by calling composable function such as state
or a combination of @Model
class wrapped inside remember
composable as in code snippet below. val labelState = state {0}
is equivalent to val labelState = remember { mutableStateOf(0) }
where both does referential equality between old and new value to prior to mutation of state.
@Composable
fun LabelWithStateComposableThatDoesReferentialEquality(){
val labelState = state {0}
Text(text = "$labelState.count",
style = MaterialTheme.typography().body1)
}
Otherwise if you want to do structural equality on state mutations, you can possible achieve by through the following.
val labelState2 = remember { LabelState(0) }
is equivalent to val labelState2 = remember { mutableStateOf(value = 0, areEquivalent = StructurallyEqual) }
where both does structural equality between old and new value to prior to mutation of old state value.Leland Richardson [G]
02/20/2020, 7:38 PMIn case where a composable function is called with the same inputs multiple times, compose runtime will check against the Slot table whether the same inputs have corresponding outputs that were previously cached. If yes, then the compose runtime will retrieve the previously cached results otherwise insert new inputs and their corresponding results to the Slot Table.This is more or less correct, but there are some nuances. We call this “skipping” in that we will “skip” the execution of a composable function if certain criteria is met where we think it is safe to do so. That criteria is something we are constantly evaluating and may shift over time as our compiler gets smarter or we change implementations. Right now, one big limitation here is that we are only skipping when all of the parameters you passed into the composable function are either @Model, @Immutable, or a “primitive” type. We may relax this constraint in the future, but we would rather start in a conservative place since “false positives” here can significantly reduce users trust in the system. Another side effect of this is that currently lambda literals are almost guaranteed to break the “skippability” of a composable, however we are working on a very significant optimization that will allow us to treat lambdas differently.
Given that understanding, it means that theI’m not sure what you mean by this, but let’s use an example… If i write the code:passed as input parameter to a composable in the following code snippet will be positionally memoized despite that is is not wrapped insideLabelState
composable.remember
LabelThatPassStateAsParameter(LabelState(0))
Each time this code runs, the LabelThatPassStateAsParameter
composable will also run because I am creating a new LabelState each time. The function will compare the new instance with the old instance and find that they are different, so it will re-execute itpula
02/20/2020, 7:48 PMLeland Richardson [G]
02/20/2020, 8:11 PMequals
method that the data class generated caused the parent scope to get subscribed (since it was a read)var
properties is arguably an anti-patternKlaas Kabini
02/21/2020, 6:08 AMLeland Richardson [G]
02/21/2020, 6:50 AM