I'm prototyping an alternate way of managing state...
# compose
v
I'm prototyping an alternate way of managing state in UI state holders by trying to exclusively use snapshot
State
and
derivedStateOf
, since
combine
is somewhat clumsy to use, and flow operators lose the stateful type (
StateFlow<T>.map(): Flow<T>
). Is this a bad idea for some reason? It would also avoid having to have a wrapper composable just to collect flows to state. I got the idea for this from a comment in GitHub by Adam Powell. Example in 🧵
🧐 1
A collector function that doesn't need a composable context:
Copy code
fun <T> StateFlow<T>.collectAsState2(scope: CoroutineScope): State<T> {
    val state = mutableStateOf(value)
    scope.launch {
        collect { state.value = it }
    }
    return state
}
And usage in a state holder:
Copy code
interface IStateHolder {
    val uiState: UiState
}

class State(dataSource1: DataSource1, dataSource2: DataSource2, scope: CoroutineScope) : IStateHolder {
    private val dataSource1State = dataSource1.data1Flow.collectAsState2(scope)
    private val dataSource2State = dataSource2.data2Flow.collectAsState2(scope)

    override val uiState = derivedStateOf {
        if (dataSource1State.value == ...) {
            // Construct an UiState here based on complex logic
        }
    }

    companion object {
        @Composable
        fun rememberState(dataSource1: DataSource1, dataSource2: DataSource2): IStateHolder {
            val scope = rememberCoroutineScope()
            return remember { State(scope) }
        }
    }
}
m
The mutablestate is not remembered. And launch will be called on every composition
v
It cannot be remembered as
collectAsState2
is not a composable function and isn't called from any composable context
The idea is that the class
State
is created with
rememberState()
, so it's not recreated on every recomposition, only during the initial composition
m
Oh i get it now. You want to convert stateFlow to state and use it in the viewModel. Its fine. There is no simpler way i think. Altough i would just use combine and avoid compose state in viewmodel
Im not sure this is useful. Why not use combine and the collect and update a mutable state variable that gets exposed to outside?
v
Combine maps to a
Flow
which leaves the question of what to initialize the state with, as there is no current value guaranteed. I could always initialize with nulls, but it feels a little counterproductive when all my data sources are
StateFlows
and have a non-null value available
i
when all my data sources are
StateFlows
This feels very odd. All of your data sources are hot and have an always up to date value (that's what a StateFlow means)? That feels like a terribly inappropriate use of resources or a misuse of StateFlow. That's why data sources expose Flows, not StateFlows
v
Yeah, majority of my data sources are hot. My application is designed to run in an embedded context and the whole device is dedicated for it. A large portion of the app is controlled by hot state from a websocket connection