Is there any difference between ``` val devices...
# compose
l
Is there any difference between
Copy code
val devices = remember { mutableStateListOf<DeviceModel>() }

    LaunchedEffect(subject = viewModel.devices, block = {
        viewModel.devices.collect { model ->
            if (!devices.contains(model)) devices.add(model)
        }
    })
and
Copy code
val devices by produceState(initialValue = mutableStateListOf<DeviceModel>(), viewModel) {
        viewModel.devices.collect { model ->
            if (!value.contains(model)) value.add(model)
        }
    }
a
check the source and you’ll see how it’s different
👍 1
second one only runs a LaunchedEffect once per composition even if devices state changed, it seems
l
🤦‍♀️ my bad
@allan.conda Are you aware of an elegant way to only have unique objects in the
mutableStateList
?
👍 1
d
The second one is slightly less efficient. Or at least add a bit of unnecessary complexity.
Because you never change the state object.
1
Consider using
List<DeviceModel>
for the second case, then they become more comparable.
👍 1
a
or
PersistentList
, I think
MutableStateList
uses the persistent collections under the hood
1
d
To keep the objects unique you can use a
LinkedHashSet
.
l
Thanks guys. @Dominaezzz @Adam Powell When I use
linkedSetOf
or
mutableListOf
for second snippt, no items are shown which makes sense, because I don't do
value = device
so
devices
doesn't change but when I use
mutableStateListOf
and do
value.add(device)
,
devices
changed. Or am I wrong? Btw...it seems that
PersistentList
doesn't exist, same for
MutableStateList
?!
a
PersistentList comes from the kotlinx.collections.immutable artifact; by MutableStateList I mean the object returned by mutableStateListOf. The analogy here is
var list = mutableListOf()
- you don't need both the list and the reference to the list to be mutable
Pick one;
produceState
is about that leading
var
Your first snippet is about the collection being mutable but the reference to it immutable; probably what you want here
l
Waht do you mean with
produceState
 is about that leading 
var
a
produceState
is just a convenience around
Copy code
val state = remember { mutableStateOf(initialValue) }
LaunchedEffect {
  state.block()
}
return state
It gives you one observable mutable variable that the provided block feeds
But you don't want one observable mutable value, you want one observable mutable list
l
ok I guess I got it. In second snippet both the list and the reference is mutable
a
Yep
l
The first one fits definetely better here 🙂
a
I think so too 🙂
l
But I still have the problem with duplicates 😕
a
we've only got
mutableStateListOf
and
mutableStateMapOf
for snapshot-observable collections at the moment, we don't have a set. If you combine
PersistentSet
from kotlinx.collections.immutable and
produceState
from compose you should have what you need
which is to say,
PersistentSet
is an efficient immutable set type that potentially returns a new set instance whenever you add or remove from it, and if you store that reference in a
mutableStateOf
holder, you get the observability you need with an efficient immutable set implementation
l
@Adam Powell Couldn't I also add the device directly to the LazyColumn instead of adding it to a list/set first? 🤔
a
a LazyColumn (and any composable function, really) just reads from state you supply. Once this patch lands, you can use any backing state type you like, but there will still be no concept of adding to a data structure private to/owned by LazyColumn itself https://android-review.googlesource.com/c/platform/frameworks/support/+/1530327
l
@Adam Powell Alright thanks. Lets say a device is collected which is already in list/set but I would like to replace the new one with the existing one because the new one has updates fields, how would you achieve this?
I could remove the existing one and add the new one but this would trigger recompose twice or?
a
When you add or remove from a persistent collection, it returns a new immutable collection instance that copies as little as possible from the original while sharing internal structure. Once you perform the manipulation you need, assign the new collection to a MutableState object once.
Either way though, if you want to perform several snapshot state updates atomically with regard to one another, see
withMutableSnapshot
. Any changes you make within an explicit mutable snapshot all happen together or not at all, and changes aren't made visible to other threads unless you commit all changes together or otherwise pass the snapshot between threads explicitly.
Compose will only recompose for full snapshot commits, not for partial snapshots.
l
I still get duplicates:
Copy code
val devices = remember { mutableStateOf(persistentListOf<DeviceModel>()) }

    LaunchedEffect(subject = viewModel.deviceFlow, block = {
        viewModel.deviceFlow.collect { model ->
            if (!devices.value.contains(model)) {
                devices.value = devices.value.add(model)
            }
        }
    })

    BluetoothDeviceListComponent(devices.value, onConnectDevice)
What do I wrong? :/
d
Does
DeviceModel
have an
equals
implementation?
l
Copy code
data class DeviceModel(val bluetoothDevice: BluetoothDevice, val rssi: Int)
data classes generate equals functions right?
a
They do, so if the rssi keeps changing, they aren't equal and it will look like a new object to that code 🙂
Remove the element with the same unique id, or use a map with the unique id as the key
l
@Dominaezzz Thanks for the hint. To just get rid of the duplicates I only had to override the equals function:
Copy code
data class BluetoothDeviceModel(val bluetoothDevice: BluetoothDevice, val rssi: Int) {

        override fun equals(other: Any?): Boolean {
            if (other is BluetoothDeviceModel) {
                return this.bluetoothDevice.address == other.bluetoothDevice.address
            }
            return false
        }
   }
Additionally when I want to update the list with newer devices there are multiple options. To preserve the order of the items I came up with this solution:
Copy code
val devices = remember { mutableStateListOf<DiscoverBluetoothDevicesUseCase.BluetoothDeviceModel>() }

    LaunchedEffect(subject = viewModel.deviceFlow, block = {
        viewModel.deviceFlow.collect { model ->
            withMutableSnapshot {
                devices.indexOf(model).takeIf { it != -1 }?.let { i ->
                    devices.remove(devices[i])
                    devices.add(i, model)
                } ?: devices.add(model)
            }
        }
    })
@Adam Powell Is this implementation legit regarding performance and do I use
withMutableSnapshot
properly?
a
That'll do fine
👍 1