I have a composable function that gets 3 State<...
# compose
y
I have a composable function that gets 3 State<T> params. Is it better to send State/MutableState in params or the value inside them?
i thought sending the mutable/state is better since it maybe reduces the number/amount of recompositions but now my issue is that it looks like I cannot create State<T> manually to preview my composable
d
What does "send" mean? I think a code snippet might be necessary.
y
I meant function parameters
Copy code
fun ActiveStationItem(
    stationNullable: Station?,
    isPlaying: Boolean,
    playState: Int?,
    onTogglePlay: () -> Unit
) {
}
I have these params
Now I just send the actual Boolen, int etc variable
d
Ah I see. The convention is to just pass the values.
y
but I can also send the State
Isnt sending State/MutableState make recompositions lighter?
d
Don't worry about that. That's the compilers job to worry about. If the compose team recommends passing values, then they'll know to optimise for that case.
y
Uh okay 🤔
I thought the opposite was recommended
thanks
d
If you look at the material components, you'll see the same pattern of passing in values instead of State. It just makes testing easier.
👀 1
🙌 1
a
I keep meaning to add it to the compose API guidelines doc and make sure we get a lint check;
State<T>
and
MutableState<T>
generally shouldn't appear in parameter lists. If you really find yourself in a place where you need more granular invalidations from performing a snapshot read more locally, use a
() -> T
instead
Passing
[Mutable]State<T>
in parameter lists discourages single source of truth vs the alternatives, and breaks usage of the property delegates
🙌🏽 1
🙌 5
y
Im glad I asked 😄 I thought the opposite up to now, will update my code
m
I recently made this exact same comment in a code review. I have a best practices page setup for us, and one of the first things in there after naming conventions is to pass values, not State objects to composable functions.
a
The longer version of the reason is, consider if the caller has a more complex state object that holds the source of truth, something like:
Copy code
class Foo {
  var someValue by mutableStateOf(...)
  // ...
}

class Bar(
  private val foo: Foo
) {
  val computedValue: Int
    get() = foo.someValue * 2
}
which of these two composables with equal snapshot invalidation characteristics is easier to call with a
bar.computedValue
?
Copy code
@Composable
fun BazOne(
  value: () -> Int
)

@Composable
fun BazTwo(
  value: State<Int>
)
With the first it's just
BazOne({ bar.computedValue })
. With the second it's
Copy code
BazTwo(
  object : State<Int> {
    override val value: Int
      get() = bar.computedValue
  }
)
generally the only times you should reach for a lambda like this without profiling first though are if you're taking a frequently changing (e.g. animating) value into a later, post-composition phase and you're trying to skip composition entirely when it changes
otherwise start with passing the parameter by value
s
I keep meaning to add it to the compose API guidelines doc and make sure we get a lint check;
State<T>
and
MutableState<T>
generally shouldn’t appear in parameter lists. If you really find yourself in a place where you need more granular invalidations from performing a snapshot read more locally, use a
() -> T
instead
Has this ever made it anywhere in the official docs so far?