Hi all, was a happy user of mutableStateList but m...
# compose
a
Hi all, was a happy user of mutableStateList but my team is moving (for good reasons) to using MutableStateFlow instead of compose state to hold State in a ViewModel class. its nice in a lot of ways but updating lists of states (like text fields for instance) is a fair bit tougher! I liked having a MutableList-like api for working with this use case. Does anyone have recs for anything i can do to make my life easier? Im finding myself writing things like
Copy code
state.update { 
    it.mapIndexed { index, it ->
        if (index == change index) ... else it
    }
}
which seems wrong
could maybe write
Copy code
it.toMutableList().let { do my operations }.toList()
it feels a little less cursed but almost equally hacky
z
If you use the kotlinx.collections library, the persistent collections can produce builders to modify them and return new immutable collections.
a
all i find when googling this is the collections.immutable lib. is it bundled in automatically? can you give an example of how to get such a builder?
z
i
Note that with BasicTextField, you can't use MutableStateFlow at all without even more issues, see this article: https://medium.com/androiddevelopers/effective-state-management-for-textfield-in-compose-d6e5b070fbe5
☝🏻 1
☝🏾 1
a
ooh. fun
oh ok!
so i should feel free to use mutableListState is what you're saying 😄
i think this is plenty good justification, thanks for the intel and i'll let my team know :)
i
Zach actually had a good article about mutables in mutables as an anti-pattern: https://blog.zachklipp.com/two-mutables-dont-make-a-right/
As long as you are avoiding those cases and avoiding cases that are particularly thorny like text state entirely, you should be on the right track
a
thanks for both yr assistance
c
I think I also heard Adam Powell say that even if you use flows... you still need to technically convert to snapshot state via .asSnapshotState. so. (im guessing here) but that means that you now have a flow in memory and snapshot state in memory for the same data. so just having snapshot state "should" be cheaper (this is just an educated guess though)
also. not sure if this helps (ive never used it) but i have this bookmarked to try one day. wonder if it'd help https://github.com/kopykat-kt/kopykat
a
I’m curious what the “(for good reasons)” are for moving from snapshot state to
MutableStateFlow
1
z
well if you want to update composition, you have to ultimately do it through a snapshot state object. The easiest way to do that with a flow is
collectAsState()
, but you could also collect it in a
LaunchedEffect
and update your own states, or write to a state in some other glue code.
a
@Alex Vanyo issues with thread safety
🤔 2
1
z
did you get that state initialization exception? That should be mostly fixed in more recent 1.7 releases
c
My team just doesn't want to touch snapshot state at all unfortunately. Everything "has" to be flows 😭
i
Just a few years ago, that was teams with Rx, that all or nothing pattern isn't uncommon
☝️ 2
☝🏻 1
c
Yep. Meanwhile my personal projects just use snapshot state everywhere. love it. lol
a
it is my understanding that one of the main differences between stateflows and state is that with stateflows you guarantee atomic updates across threads
we definitely ran into issues with this updating snapshot state off the back of parallel io operations, that was fixed by moving to a StateFlow implementation
🤔 1
not sure if it's android only, this is using viewmodelscope
z
What kind of issues? Snapshot state is explicitly designed to support this, and even supports updating an arbitrary number of states atomically which MutableStateFlow does not.
K 1
a
umm, this was all a while ago but i'll try be accurate and succinct
Copy code
var uiState by mutableStateOf(UiState())
...
viewModelScope.launch {
    launch {
        ...
        uiState = uiState.copy(data)
    }
    launch {
        ...
        uiState = uiState.copy(otherData)
    }
}
sometimes one of these writes would be overwritten
👀 1
moving to a MutableStateFlow and doing
Copy code
uiState.update { ... }
instead would remove this problem
c
FWIW, I have a single UiState too, but each field itself is a mutableStateOf. This makes sure that when I change one tiny thing, the entire UiState doesn't have to change
1
z
well you're comparing two different things:
Copy code
uiState = uiState.copy(value)
is equivalent to doing the following with `MutableStateFlow`:
Copy code
uiState.value = uiState.value.copy(value)
which is also not atomic.
☝️ 1
The
update {}
function is what makes the
MutableStateFlow
update atomic. For snapshot state, you'd need to wrap the operation in a `withMutableSnapshot`:
Copy code
launch {
  Snapshot.withMutableSnapshot {
    uiState = uiState.copy(data)
  }
}
But then inside that
withMutableSnapshot
block, you can write to as many state objects as you like, whereas with
MutableStateFlow.update{}
you can only update that one flow only.
a
is this surfaced much? im convinced that this would fix the issue but its also my first time seeing anything of the sort
z
withMutableSnapshot
is mentioned in the official documentation, but it's not used in most samples because you don't need it in most cases, especially when you're doing all your state updates on the main/ui thread.
@Alex Vanyo Maybe DAC could use a section or page on using MutableState in other architectural layers?
z
oh nice
c
went back tonight finally to read through some of those docs. learned something new. thanks all for the convo!
157 Views