Trying to figure out if I need a `remember` arg I...
# compose
c
Trying to figure out if I need a
remember
arg I have
Copy code
itemsIndexed(items) { index, item ->
 val thing = remember { MyThing(item) }
}
teammate says this should be
val thing = remember(item) { MyThing(item) }
instead of my approach
val thing = remember() { MyThing(item) }
🚫 1
@agrosner "No" as in I'm right or my teammate is right?
a
No you’re right
c
I think I'm correct here because if item changes then compose should redo the memoization
a
Remember is more expensive on simple classes
It really depends on recomposing and keeping state around
c
just updated my question. i think maybe i didn't convey it correctly.
a
Ohhh. Yeah if it’s immutable then it doesn’t matter as much.
c
MyThing isn't immutable. It's (unfortunately) an expensive service class that does things in the background with
item
that's passed in.
a
If items list changes without a key I believe all is lost iirc
So either way doesn’t quite matter
Passing item into remember will recreate it if you added key to items indexed call and sent a new list with same item key and the item itself has something that changes like another field
e
I think the answer is that it depends on what you want to do. By passing the item to remember, then
MyThing
will be recomputed if the item or the item's position changes. If not, then the same
MyThing
will be returned regardless of the item at the current index.
c
So
val thing = remember(item) { MyThing(item) }
== MyThing will be recomputed if the item or the item's position changes
val thing = remember() { MyThing(item) }
== the same MyThing will be returned regardless of the item at the current index ?
a
If items can be added to or removed from the list, you do need the key (or you can also specify the key in
itemIndexed
), otherwise the remembered
MyThing
can be different from the actual item after list items change.
c
The list is a snapshot state list and yes they can be added or removed. Damn okay. I guess I still had a misunderstanding of how this was going to work. i assumed that if the item changed, then essentially the entire block was going to be re-run.
a
Specifying a key in
itemIndexed
is the better way as it prevents the need to recreate
MyThing
.
âž• 1
c
okay. my items do have an id, so I guess I will use that in the args for itemsIndexed
lol. for what its worth (im coming into this project pretty cold) and it looks like somewhow this MyThing "service" actually extends like 5 times, and the root class it extends from is an AAC ViewModel.
so I guess the original question should have been "is this right?"
Copy code
itemsIndexed(items) { index, item ->
 val thing = remember { MyViewModel(item) }
}
To which we learned it should instead be
Copy code
itemsIndexed(items, { index, item -> item.id }) { index, item ->
 val thing = remember { MyViewModel(item) }
}
👌 1
Okay, my next thing I'm going to do is I think pull all of the code out of
MyViewModel
and into
MyActualScreensViewModel
and therefore I won't have this issue of creating this thing on the fly. But overall I think I learned something here... so thank you all!
s
if
itemsIndexed
is the lazy list extension, then your thing works only when the key is specified, because compose reset composition completely when reusing items
if the key is not specified, it is equivalent to using item index as a key, so if item index changes you'll get stale
remember
value, as commenter above mentioned
fwiw, if you were to pull this into a separate composable, i would suggest to use
remember(item)
. Using
remember
without keys is only technically correct in lazy since you can guarantee that only one
item
is going to be present
c
if itemsIndexed is the lazy list extension
yes this is in a lazyColum
if the key is not specified, it is equivalent to using item index as a key
🤯
Using remember without keys is only technically correct in lazy since you can guarantee that only one item is going to be present
🤯 Why do I feel like I've been using remember all wrong for the past 5 years. lol
really seems like https://developer.android.com/develop/ui/compose/state could be updated with some more clarity regarding this
i think my misconception was... in a lazy list once my composable is rendered, that the remember'd value is calculated... and once it goes off the screen and comes back it gets re-calculated. so in this case, thats why i thought this would be fine.
Copy code
itemsIndexed(items) { index, item ->
 val thing = remember { MyViewModel(item) }
 Column {
  Text(item.toString())
  Text(this.currentStopWatchValue())
 }
}
FWIW. I was able to come up with a 50 line example showing this for anyone thats following along https://gist.github.com/ColtonIdle/71ca77d54f84617ac897f101582df96c (much easier to see the issue in this example vs my complicated codebase) indeed the only thing that "fixes" this is putting a key on itemsIndexed(items, KEY). If I put a key on
val thing = remember(KEY) { MyViewModel(item) }
it did not work.
s
I am surprised it didn't work when you put the key in remember, it should recreate the instance
c
Feel free to look at my code and try it out there if it piques your curiosity!