Why does this keeps recomposing `Button` slot's co...
# compose
d
Why does this keeps recomposing
Button
slot's content and printing "recomposing"?
Copy code
MaterialTheme {
  val handler = { _: TextLayoutResult -> }
  MyButton(handler)
}

@Composable
fun MyButton(handler: (TextLayoutResult) -> Unit) {
  val state by flowOf("two", "three").collectAsState("one")
  Button(onClick = {}, enabled = state == "two") {
    println("recomposing content")
    Text(text = "Button", onTextLayout = handler)
  }
}
If I remove "handler" parameter recomposition happens only once. Is there a way to somehow mark "handler" as stable?
a
Try using
remember { flowOf("two", "three") }.collectAsState("one")
?
d
ohh, right. I was investigating a weird recomposition which happens if I pass a lambda into a slot. Tried to come up with a minimal example and produced a compromised one 🙂 Will try further. In my case state flow is not recreated — for sure
h
I am not sure if a lambda supports
equal
. I think, setting a lambda variable inside a recomposition is not supported.
I would move the lambda outside of compose functions
d
But lambda is an object, so
handler == hanlder
will be
true
Ok, I've come up with another sample:
Copy code
fun Application() {
  val handler = { _: TextLayoutResult -> }
  MaterialTheme {
    val state by remember { flowOf("two", "three") }.collectAsState("one")
    MyButton(state = state, handler = handler)
  }
}

@Composable
fun MyButton(state: String, handler: (TextLayoutResult) -> Unit) {
  Button(onClick = {}, enabled = state == "two") {
    println("recomposing content")
    Text(text = "Button", onTextLayout = handler)
  }
}
This uses
remember
and it keeps recomposing the slot even though handler doesn't change and slot uses only handler. This keeps happening even if I move
handler
to be a top-level file scoped variable
a
If the dependencies of a lambda didn’t change, the same lambda instance will be used so it
equals()
the previous one.
d
In my latest sample above lambda is the same, but it still recomposes for some reason. this happens even if I modify sample to pass
() -> Unit
lambda to some
onClick
inner to the slot.
a
d
Oh, nice hint, thanks! I'll try to use this, heard of it, will read up. I hope it works with 1.1.x compose
a
yep we use the metrics with 1.1.x of compose
the tooling around it is not available until newest android studio canary I believe
but you can still read the output
s
I think lambdas should be optimized in that case to keep the same instance (it doesn't capture any values) About flow, and collecting as state, I believe you restart it on every composition, because you remembered flow, and not resulting state, resulting in re-subscription to flow and recomposition as state updates each time. Most likely, you are looking for:
Copy code
remember { flowOf(...).collectAsState("one") }
Note that state is created inside remember in that case.
It still looks a bit weird to me, as handler never changes, so it shouldn't really be recomposing Most likely it triggers something inside measure pass that forces recomposition, but not sure what exactly