:android-wave: Is there a way to determine when a ...
# compose
c
👋 Is there a way to determine when a key has changed and when that is true run certain blocks of code in a composable? For example, I want to run a certain block of code only when
scrollState.isScrollInProgress
changes from false to true or true to false. I don't want to use a LaunchedEffect with a key to avoid creating a new CoroutineScope everytime someone starts/stops scrolling.
f
DisposableEffect
has a key parameters and does not create a Coroutine scope
👍 1
c
True! I was hoping there would be some kind of mechanism that didn't involve a side effect but maybe this use case is too fringe to warrant something like that. That's definitely the lesser of the two evils, ty 🙂
z
The main problem with something like
LaunchedEffect(scrollState.isScrollInProgress)
is not that you’re creating a coroutine for every time scroll starts/stops – that’s perfectly find (the entire animation system works that way, including scroll flings). The problem is that it requires recomposition unnecessarily. I think this is what you’re looking for:
Copy code
LaunchedEffect(scrollState, …) {
  snapshotFlow { scrollState.isScrollInProgress }
    .collect { isScrolling ->
      …
    }
}
👌 1
☝️ 1
c
That was actually what I ended up doing. And just to make sure, the reason it's recomposing unnecessarily is because it's needing to read the value of
scrollState.isScrollInProgress
over and over?
Edit: Nvm, the recomposition is on the LaunchedEffect, I see.
z
Yea, if you’re reading
isScrollInProgress
in the composable in order to pass it to
LaunchedEffect
as a key, then that’s going to recompose every time that state changes.
👍 1
c
Hey thanks for responding to so many people's questions. In case no ones thanked you recently it really helps 🙂
1
z
It was a nice break from my regular job this morning 😛
c
I can only imagine 😨
j
totally agree with that “thanks” message! between Zach and Jossi, I think I’ve been able to avoid many mistakes in learning Compose very much appreciated! 💙
I had this answer bookmarked for myself to come back to when I had a chance to compare to some of my code, and I had a further question on this, if I may bug you Zach? I am currently achieving the same thing with the following code, which works as intended:
Copy code
val isScrolling by remember {
    derivedStateOf { scrollState.isScrollInProgress }
}
if (isScrolling) {
    LaunchedEffect(isScrolling) {
        // ...
    }
}
is this an okay practice? or should I be changing my code to something like the snapshotFlow example you posted above?
m
I dont umderstand how both solution works. Both snapshot and derivedState work on State<>, but scrollInProgress is just a regulat field.
a
I don’t think there’s any point in using
derivedStateOf
here as
remember { derivedStateOf { scrollState.isScrollInProgress } }
is the same as
scrollState.isScrollInProgress
if
scrollState
doesn’t change (which is likely the case). @myanmarking
scrollState.isScrollInProgress
comes from a snapshot state and will trigger recomposition.
m
Will trigger on scrollstate change or on the boolean itself?
In that case, derivedstate scrollinprogress might make sense then?
a
On both cases. However,
scrollState
is unlikely to change as it is normally created by
rememberScrollState()
and in that case
derivedStateOf
makes no point.
z
There’s no reason to use
derivedStateOf
simply to read the value of
isScrollInProgress
, since the rate of change is the same. Where it makes sense to use
derivedStateOf
with scrolling is, for example, if you need to calculate some condition based on the scroll value. In that case, the rate of change of the scroll state is very frequent – every frame during a scroll/fling – but the rate of change of the derived state is much lower. E.g.
Copy code
val isNearTop by remember(scrollState) { derivedStateOf { scrollState.value < 10 } }
Re: using a
snapshotFlow
vs keying a
LaunchedEffect
off the changing value, the latter requires more unnecessary recomposition than the former. If the rate of change is low, as it is for
isScrollInProgress
, that’s a negligible cost. But it’s also just more moving parts, and imo less elegant. When the value changes, you trigger a recomposition, that starts a new effect, which starts a new coroutine, which will get torn down when the value changes again. Vs with
snapshotFlow
, you start one coroutine, it runs continuously, and the only thing that happens when the state changes is a new flow emission. I’m guessing the
snapshotFlow
approach will also scale better if your logic grows in complexity.
j
excellent, thanks so much for the follow up explanation 🙏