Hi, I need to run a side-effect whenever a compose...
# compose
f
Hi, I need to run a side-effect whenever a compose
mutableStateOf
variable changes. I see two ways of achieving this, either have a
LaunchedEffect(Unit)
and inside subscribe to changes in the state with
snapshotFlow
or have a
LaunchedEffect(stateVariable)
which then executes whenever the variable changes. Now I don't actually need a coroutine scope for my effect so the second option seems weird to me because I'm not using the provided scope. The first option looks a bit fishy because of the
Unit
as the key for the effect. What's the correct way to do this? Or maybe I'm doing something wrong from the get go? Thanks!
k
I usually use second choice.
f
even when you don't need a coroutine scope?
the docs seem to indicate that you should use LaunchedEffect for when you want to launch a coroutine
k
Should side-effect return some different value? If so, you could use
derivedStateOf
, as
remember
also takes a key and reacts to change.
s
I don’t think using
derivedStateOf
to trigger side-effects is a good idea. the lambda passed into it as far as I understand may be triggered more than you think, it’s just that the returning `State`’s value is not changes as often, leading to the performance benefits of it. Unless I am misunderstanding what you mean by side effects here Fabio. It may be useful to tell us what you want to do with that change.
f
yeah so basically I have a video object I want to call
.play()
and
.pause()
on depending on some state
In that sense, I don't need to derive new state, so I think
derivedStateOf
is not the right call
b
I would use snapshotFlow for this, key the launchedeffect with your player not with Unit. It would be slightly more efficient, you would be launching once where as if you do LaunchedEffect(state) you are launching every time that state changes which is more expensive.
s
I would like to know more about this, too -
snapshotFlow
doesn't seem cheap. However, when it comes to invoke
Animatable.snapTo
, the samples/demos always wrap that in
coroutineScope.launch
(so it ends up launching a lot of times when something like a drag gesture occurs)...
s
Not sure about the second part, but for snapshotFlow not seeming cheap, I think the comparison here is with changing the key of a LauchedEffect, therefore cancelling the old coroutine and starting with the new coroutine, which is certainly more expensive than simply having one coroutine running which has only the job of collecting the latest changes from a State, without being cancelled on each state change.
z
“SnapshotFlow doesn’t seem cheap” Some questions: 1. Why not? 2. The alternative proposed here is triggering recomposition AND launching a new coroutine - does that seem cheaper? Triggering recomposition does everything snapshotFlow does and quite a bit more. So for the same use case, all else being equal, snapshotFlow is definitely cheaper than triggering a new LaunchedEffect on every change.
Even more important than performance though, which is unlikely to even be significantly impacted by the “worse” option here, is correctness. If you observe with snapshotFlow, the side effect can run effectively immediately, and guaranteed before the next frame. If you observe by recomposition, the side effect won’t run until strictly after the next frame, so any other UI updates triggered by your side effect would be a frame late.
f
thanks for your inputs! I'm wondering, maybe there should be an effect function that doesn't start a coroutine but runs when keys change (unlike SideEffect which runs on every recomposition)?
k
Running a coroutine should be very cheap.
z
There’s DisposableEffect, but that effect will only run before layout on the current frame, and after all recompositions are done.
f
yeah but if there is nothing to dispose, it seems weird to use? Anyhow, it seems like snapshotFlow is the way to go for my problem at hand here, thanks!
z
Again, using recomposition purely to watch for snapshot state changes, if those state changes don’t actually require changing the composition, is a waste of computation and actual wall clock time.
snapshotFlow
is way more lightweight.