I'm trying out the `molecule` library and migratin...
# compose
d
I'm trying out the
molecule
library and migrating my presenter code into
composable
function. But some function that have primitives parameter recomposed multiple times when it doesn't need to be. code in thread I expect
presentCountryListState
to be recomposed everytime either
initialState
is changed or
query
is changed. This is true, it will always recompose when query changed but when I look at the console everytime query changed this log shows:
Copy code
Recomposition for presentCountryListState
Recomposition for present
Recomposition for presentCountryListState
Recomposition for present
Notice that it run the function twice somehow Anyone know why this is happening?
🧵 2
I've commented out the
val initialState by searchCountryUseCases.getSearchCountryCodeInitialState().rememberAndCollectAsState(listOf())
and the recomposition still happens twice
Snippet: the snippet code:
Copy code
@Composable
    override fun present(event: Flow<Event>): State {
        var searchBox by remember { mutableStateOf("") }
        var selectedCountry by remember { mutableStateOf<CountryCodeModel?>(null) }
        val countryListState = presentCountryListState(searchBox)
        println("Recomposition for present")

        CollectEffect(event) {
            when (it) {
                is Event.ItemClicked -> {
                    selectedCountry = it.item
                    saveRecentCountryUseCase(it.item.code)
                    searchBox = ""
                }

                is Event.SearchBoxChanged -> searchBox = it.query
            }
        }

        return State(
            searchBox = searchBox,
            countryListState = countryListState,
            selectedCountry = selectedCountry
        )
    }

    @Composable
    private fun presentCountryListState(query: String): State.CountryListState {
        println("Recomposition for presentCountryListState")

        val initialState by searchCountryUseCases.getSearchCountryCodeInitialState().rememberAndCollectAsState(listOf())
        var result: State.CountryListState by remember { mutableStateOf(State.CountryListState.Loading) }

        if (query.isEmpty()) {
            result = if (initialState.isEmpty()) {
                State.CountryListState.Loading
            } else {
                State.CountryListState.Success(initialState)
            }
        } else {
            LaunchedEffect(query) {
                result = State.CountryListState.Loading
                result = filterCountry(query)
            }
        }

        return result
    }
z
Every time query changes you’re setting the state to loading and then to the result of the filter operation. This seems like it would cause two recompositions if filterCountry suspends before returning.
d
@Zach Klippenstein (he/him) [MOD] it makes sense But why it also happen when
query.isEmpty
? Since there's no suspend function and changing to loading there, the recomposition also happened twice
z
Does that also recompose twice if you comment out the
getSearchCOuntryCodeInitialState
line?
What does
CollectEffect
look like?
d
Yep it still happens. CollectEffect is just a shortcut for
Copy code
LaunchedEffect(event) {
 event.collect {...} 
}
Copy code
fun CollectEvent(event: Flow<Event>, action: (Event) -> Unit) = LaunchedEffect(event) {
  event.collect { action(it) } 
}
z
CollectEvent has a bug, if you pass in a different
action
lambda in a recomposition it won’t be used by the collector. That implementation is so simple though I think wrapping it in a function actually obscures the code and makes it harder to read, I would just inline it. Anyway, I don’t think that has anything to do with your double recomposition.
Stepping back for a second, why do you care about the double recomposition? Is it having a measurable effect on performance?
I don’t see anything in
present
or
presentCountryListState
that should trigger immediate recomposition after the initial composition, but since both those functions return a non-Unit value, neither of them get their own recompose scope. Whatever’s calling
present
might be doing something to trigger the recomposition.
d
@Zach Klippenstein (he/him) [MOD] For the collect event bug somehow it still works as expected as I still able to receive all the event that I got. Will note that. For why i care about double recompostion, I'm exploring if there's a possibility the
skippable composable
can simplify a rective code. Example above, i want to see wether the composable will be skipped if the
query
value is the same. I can achieve similar impact with
remember(query)
but I want to shorten the code. If double recomlosition happening, that mean I cannot use skippable composable for my advantage. I'm a bit confused too, I'll create a sample code for you later. The one that calling present is just a simple composable function, I've made multiple approach to call
present
one of them is using molecule which wrap it in independent composable scope and the double recomposition still happening
z
It’s never a good idea to rely on composable skipping or recomposition specifics for correctness. Your composables should be able to recompose any number of times without breaking your code.
d
i see, okay i'll drop the idea then. Thank you