cashapp / molecule

Build a StateFlow stream using Jetpack Compose
https://cashapp.github.io/molecule/docs/1.x/
Apache License 2.0
1.78k stars 76 forks source link

Debouncing state emissions #336

Open TheKeeperOfPie opened 7 months ago

TheKeeperOfPie commented 7 months ago

I'm trying to migrate

val data = snapshotFlow { buildFromState() }.debounce(1.seconds)
...
data.collectAsState(null)

to remove the view side default null value.

But there's no easy way to debounce in Molecule. I think the closest you can get would be

val debouncedFlow = snapshotFlow { buildFromState() }.debounce(1.seconds)
val state = scope.launchMolecule { 
    debouncedFlow.collectAsState(buildFromState()).value
}

but this moves from Compose (the actual state) -> Flow (snapshotFlow) -> Molecule -> Compose (collectAsState) -> Compose UI, which is very inefficient compared to reading the state directly inside Molecule. It also requires calling buildFromState() redundantly.

If there was a way to control/throttle the recomposition timing in Molecule, then I could try and make it only recompose every second or when the debounce concluded.

jingibus commented 7 months ago
@Composable
fun <T> debounce(value: T, duration: Duration): T {
  // ????
}

This can't be implemented on a StateFlow, but it should be implementable in compose

JakeWharton commented 7 months ago

I think that perhaps we want to consider accepting a frame clock even when using immediate mode. This would allow you to use an at-most-once-a-second frame clock either directly, or with the immediate mode. It would apply the back pressure to the recomposition rather than allowing it to run unbounded simply to discard its results.

jingibus commented 7 months ago

I think that perhaps we want to consider accepting a frame clock even when using immediate mode.

I would think this would be equivalent to using the frame clock directly. What advantage does this give us over providing some utility code to create frame clocks from Flow<Unit>, or something similar?

JakeWharton commented 7 months ago

I was thinking that there was some advantage to still using the immediate mode such as immediately running if the period had already expired, but you're right that's just clock implementation details.