Fudge
09/18/2021, 2:26 PM@Composable fun MyComponent(map: Map<Int, String>) {
val states = map.mapValues {(k,_) -> remember(k) {mutableStateOf(0) } }
for ((k,v) in map) MyStatefulComponent(v,states[k]!!)
}
On a first look, this looks fine. We host the states for map.size
items of map
in a parent component MyComponent
, and create map.size
child `MyStatefulComponent`s, each with its own state.
However, on a second look, this code makes no sense. The way remember(key)
works, is that whenever a new value is passed, the state is erased and something new is remembered. So if there are at least two elements, the state of all elements except the last one will be erased.
In practice, hocus pocus, it works. Each element of map
has its own unique state cell. No clue how.
If a child gets added to or removed from map
, it updates and keeps working. Magic!
But, in my case that I can't reproduce here, the state gets deleted whenever the map shrinks. So I want to understand: what actually makes this work?Adam Powell
09/18/2021, 3:00 PMAdam Powell
09/18/2021, 3:01 PMremember(k) { mutableStateOf(0) }
you'll want to use key(k) { remember { mutableStateOf(0) } }
Adam Powell
09/18/2021, 3:02 PMAdam Powell
09/18/2021, 3:04 PMAdam Powell
09/18/2021, 3:05 PMComposer
interface)Adam Powell
09/18/2021, 3:08 PMAdam Powell
09/18/2021, 3:11 PMval count by if (someBoolean) {
remember { mutableStateOf(0) }
} else {
remember { mutableStateOf(0) }
}
then any time someBoolean
changes, count
will appear to "reset"Adam Powell
09/18/2021, 3:12 PMAdam Powell
09/18/2021, 3:12 PMAdam Powell
09/18/2021, 3:13 PMAdam Powell
09/18/2021, 3:13 PMAdam Powell
09/18/2021, 3:17 PMAdam Powell
09/18/2021, 3:17 PMAdam Powell
09/18/2021, 3:19 PMkey() {}
does this by inserting a movable group. When a movable group is encountered it can be reordered with its immediate siblings rather than being overwritten entirely if expectations aren't precisely met.Adam Powell
09/18/2021, 3:20 PMAdam Powell
09/18/2021, 3:26 PMremember
with a key depends not just on the position in the composable function (from the shape of the groups around it emitted by the compiler plugin) but also the order of its execution when it comes to things like iteration. If you need composition to reorder things for you, use the key() {}
composable.Fudge
09/18/2021, 3:28 PMkey
in my case fixed my issue.
But still, in the case I don't use key
in the mapValues
call, how does compose know how to save the value for each element of the map separately. Where are 'groups' added there?Adam Powell
09/18/2021, 3:29 PMremember
is just writing key/value pairs into the composition whenever it's called. The mechanism of remember keys and replaceable group keys is basically the same though.Fudge
09/18/2021, 3:30 PMremember
in one placeAdam Powell
09/18/2021, 3:31 PMif (key == composition[i++]) {
return composition[i++]
} else {
return rememberBlock().also { composition[i++] = it }
}
Fudge
09/18/2021, 3:33 PMAdam Powell
09/18/2021, 3:35 PM@Composable
functions have their own internal groups they add, yes. non-@Composable
functions don't need them since they can't read or write the composition anyway.Fudge
09/18/2021, 3:35 PMonlySometimes { val value = remember { 0 } }
val value = remember { 0 }
Fudge
09/18/2021, 3:36 PMvalue
could gain the value of the first one, since its suddenly moved from index 1 to index 0Adam Powell
09/18/2021, 3:36 PMonlySometimes
function body itself will add its own groups as needed, and since the trailing lambda function you passed it has to be @Composable
(otherwise it couldn't remember
) it does too.Adam Powell
09/18/2021, 3:36 PMFudge
09/18/2021, 3:37 PMAdam Powell
09/18/2021, 3:38 PMval lambdaToUse = if (something) {
{ val value = remember { ... } }
} else {
{ val value = remember { ...2... } }
}
onlySometimes(lambdaToUse)
then the identity of those composable function lambdas are different, as determined by their internally generated keys from the position in the source code, so they won't end up sharing state when it switchesAdam Powell
09/18/2021, 3:39 PMAdam Powell
09/18/2021, 3:41 PM@ReadOnlyComposable
around the compose codebase, now you've got the idea of what it's for - it tells the compiler plugin it can skip adding some extra groups because it's making a promise that it only does things like reading CompositionLocals, it doesn't write any data into the composition that would need to be tracked and compared later.Daniele Segato
09/21/2021, 7:35 AM