https://kotlinlang.org logo
#compose
Title
# compose
f

Fabio Berta

01/17/2023, 9:28 AM
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

K Merle

01/17/2023, 10:32 AM
I usually use second choice.
f

Fabio Berta

01/17/2023, 10:49 AM
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

K Merle

01/17/2023, 10:54 AM
Should side-effect return some different value? If so, you could use
derivedStateOf
, as
remember
also takes a key and reacts to change.
s

Stylianos Gakis

01/17/2023, 10:57 AM
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

Fabio Berta

01/17/2023, 11:01 AM
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

Ben Trengrove [G]

01/17/2023, 9:59 PM
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

ste

01/18/2023, 1:39 PM
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

Stylianos Gakis

01/18/2023, 2:19 PM
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

Zach Klippenstein (he/him) [MOD]

01/18/2023, 4:33 PM
“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

Fabio Berta

01/20/2023, 1:01 PM
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

K Merle

01/20/2023, 1:03 PM
Running a coroutine should be very cheap.
z

Zach Klippenstein (he/him) [MOD]

01/20/2023, 10:21 PM
There’s DisposableEffect, but that effect will only run before layout on the current frame, and after all recompositions are done.
f

Fabio Berta

01/20/2023, 10:39 PM
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

Zach Klippenstein (he/him) [MOD]

01/21/2023, 1:56 AM
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.
8 Views