```@Composable fun MyScreen(viewModel: MyViewModel...
# compose
u
Copy code
@Composable
fun MyScreen(viewModel: MyViewModel) {
    val state by viewModel.state.collectAsState()
    Scaffold {
        Column(
            modifier = Modifier.fillMaxSize()
        ) {
            Toolbar(
                titleRes = BR.string.main_title,
                onBackClick = viewModel::backClick
            )
            Text(state.title)
        }
    }
}
or
Copy code
@Composable
fun MyScreen(viewModel: MyViewModel) {
    Scaffold {
        Column(
            modifier = Modifier.fillMaxSize()
        ) {
            val state by viewModel.state.collectAsState()
            Toolbar(
                titleRes = BR.string.main_title,
                onBackClick = viewModel::backClick
            )
            Text(state.title)
        }
    }
}
is there any difference in terms of recomposition/performance between the two, as to where the
state
is collected? I read that when
state
changes the nearest composable parent is found and only that is recomposed, correct? So by that logic n.2 is superior, where it only recomposes the
Column & children
, right? Whereas the first one recomposes the whole screen (obviously most of it will be no-op, but still, it has to do the diffing, right? probably the performance is negligible, but help me establish a the correct pattern
a
In this specific case, there’s no difference in terms of performance because only the scope in which a state is read will be recomposed when the state changes.
collectAsState()
converts a flow into a state, but doesn’t read the state. That said, the general idea that putting a state higher in the hierarchy can potentially result in more recompositions is correct.
u
okay so just to clarify, the algoritm will find the "highest" read, which is
state.title
and then look for a parent, which is the
˙Column
and recompose that?
a
Inline functions such as
Column
will not introduce a recompose scope. Read this post for more details.
u
hmm interesting, why is it inline then? feels like a downside, no? Since now
Scaffold
needs to be recomposed
a
Calling composable functions isn’t free. Making some commonly used layout primitives inline can improve the performance of composition. I believe the decision is supported by data.
u
So having my view as a ColumnScope extension, when already in Column at parent level. is useless?
a
What do you mean by “useless”?
If you mean your function call won’t introduce a recompose scope, you are wrong. Only the body of an inline function is inlined, and inline won’t spread. As long as your function is not inline, it will introduce a recompose scope.
x
> ...
collectAsState()
converts a flow into a state, but doesn’t read the state > If you have
val state = viewModel.state.collectAsState()
you're not reading the state, but if you use
by
then it does, as it resolves the
State<UiModel>
wrapper.
Using
val state by viewModel.state.collectAsState()
does read the state, so I would expect both code snippets to behave a little different in terms of recomposition:
• If you read the state at the start of the function, the closest recomposition scope is
MyScreen
• If you read the state inside
Scaffold
then the closest recomposition scope is the one of
Scaffold
I was wrong in this, thanks @Albert Chang for pointing out
a
No. The definition of a variable using property delegate doesn't read the state. The definition is essentialy the same as this (with the only difference being that property delegate can be used inside a function):
Copy code
val _state = viewModel.state.collectAsState()
val state
    get() = _state.value
That's how property delegate works.
x
You're right @Albert Chang, the type resolves the wrapper but it's read on variable call. I was wrong then