Is it correct to use this strategy to group some U...
# compose
p
Is it correct to use this strategy to group some UiState variables into a subclass and access them? For adding data into an array I tryed doing a state.copy inside a state.copy, and it feels strange to do it... maybe this is not the correct way to manage this. I'm expanding the question in a thread with the sample.
I mean, changing this:
Copy code
data class UiState(
    val loading: Boolean = false,
    val data: List<Item> = emptyList(),
    val favorites: List<Int> = emptyList(),
    val selectedItem: Item? = null
)
Into this:
Copy code
data class UiState(
    val loading: Boolean = false,
    val mapState: MapState = MapState()
)
data class MapState(
    val data: List<Item> = emptyList(),
    val favorites: List<Int> = emptyList(),
    val selectedItem: Item? = null
)
for example, previously, for adding favorites, I did this:
Copy code
_uiState.value = _uiState.value.copy(
    favorites = _uiState.value.favorites + item
)
now, with the separated class... is this the correct approach?
Copy code
_uiState.value = _uiState.value.copy(
    mapState = _uiState.value.mapState.copy(
        favorites = _uiState.value.mapState.favorites + item
    )
)
z
Is it correct
It's valid, "correctness" is more a matter of preference here. You're free to break up your values however you see fit
d
Also worth noting that if you care about concurrency (judging by the
.value
call there this is a StateFlow) you might want to use
.update
as described here https://www.droidcon.com/2021/08/25/make-sure-to-update-your-stateflow-safely-in-kotlin/
p
@Zach Klippenstein (he/him) [MOD] it's the only way I found to achieve it, do you know another one? to me feels very rare to call a copy inside a copy, code seems to be complex and hard to understand for me
z
i agree it gets pretty messy
i believe "lenses" are one way to clean that up, but i've never used them
p
I don't know what are lenses
z
I’m not equipped to explain them. Basically “how to modify small parts of deeply nested immutable structures” is a problem that has been more or less solved in functional programming. Kotlin does not have a good built in solution unfortunately.
For compose, I would suggest just making your ui state mutable instead
p
do you mean having the variables inside the nested mapState class vars with mutablestateof? and not being the nested class itself a data class? but if I can remember that doesn't follows the recommended compose principles
m
The Arrow library has really good docs on Lenses (and other FP concepts that Arrow tries to solve in Kotlin). Obviously written from the perspective of Arrow’s API (so might not be practical to apply). I’ve found them useful for understanding some of the more general FP concepts! https://arrow-kt.io/learn/immutable-data/lens/ (and the intro section on “Optics”)
z
do you mean having the variables inside the nested mapState class vars with mutablestateof? and not being the nested class itself a data class? but if I can remember that doesn't follows the recommended compose principles
Yes that is what I mean and it absolutely follows compose principles, it’s intentionally designed to support exactly that.
p
I'm sorry, but I don't exactly understand what you mean. You mean doing this?
Copy code
data class UiState(
    val loading: Boolean = false,
    val mapState: MutableMapState = MutableMapState()
)

data class MutableMapState(
    var data: MutableState<List<Item>> = mutableStateOf(emptyList()),
    var favorites: MutableState<List<Int>> = mutableStateOf(emptyList()),
    var selectedItem: MutableState<Item?> = mutableStateOf(null)
)
If you refeer to this, Is not breaking the compose immutable recomendation for uistates? Or you mean another thing? can you show me please?
z
As written that does violate principles, because you're storing `MutableState`s in mutable properties. You'd need something like this:
Copy code
class MutableMapState(
  data: List<Item>,
  favorites: List<Int>,
  selectedItem: Item?
) {
  var data by mutableStateOf(data)
  var favorites by mutableStateOf(favorites)
  var selectedItem by mutableStateOf(selectedItem)
}
p
it's the first time I see that, and certainly I don't see it as a simpler way to achieve it, don't seems to be too much intuitive, I'm not sure that is a better option. How should I update that? having it on the immutable uistate:
Copy code
data class UiState(
    val loading: Boolean = false,
    val nutableMapState: MutableMapState= MutableMapState()
)
class MutableMapState(
  data: List<Item>,
  favorites: List<Int>,
  selectedItem: Item?
) {
  var data by mutableStateOf(data)
  var favorites by mutableStateOf(favorites)
  var selectedItem by mutableStateOf(selectedItem)
}
And having this uistate:
Copy code
private val _uiState = MutableStateFlow<UiState>(UiState(loading = true))
val uiState: StateFlow<UiState> = _uiState
for example, being on the viewmodel, how to update data list with one more item? or how to set selectedItem?
z
you just set the property
_uiState.update { apply { mutableMapState.data += newItem } }
granted, mixing MutableStateFlow and snapshot state can get sticky. If you go this route I'd probably try to make all the state snapshot-based instead. That
update
prevents race conditions, but wouldn't be needed if
_uiState
were also snapshot state.
p
Please, can you show me how you mean with my sample?
Unfortunatelly I don't understand exactly what you mean
z
Copy code
private var _uiState by mutableStateOf(UiState(loading = true))
val uiState: UiState get() = _uiState
p
Oh, you mean not using Flow anymore for managing the uistate
but the guides on android are recommending flows for uistate
z
¯\_(ツ)_/¯