Marcello Galhardo

12/16/2021, 9:09 PM
Reading the docs from
it says:
To share Compose state with objects not managed by compose, use the 
 composable, as it’s invoked on every successful recomposition.
If a
is called on “each recomposition”, what is the difference between the two codes:
fun sample(user: User) {
    // 1. With Side Effect, called each successful recomposition.
    SideEffect { analytics.setUserProperty("userType", user.userType) }

    // 2. No SideEffect, called each recomposition.
    analytics.setUserProperty("userType", user.userType)
My understand is that a
would not be triggered if a recomposition happens (similar to
and how it works with the keys) but based in the docs I think I’m missing something…

Casey Brooks

12/16/2021, 9:26 PM
I'm not 100% sure, but my guess is the important thing to note with using the
function is it's called on every successful recomposition. So if there's an exception or something else that might interfere with the recomposition, the
doesn't get called, helping to avoid leaving your application in a bad state.

Marcello Galhardo

12/16/2021, 9:33 PM
Hmm, well noted - I’m not familiar with the concept of “unsuccessful recomposition”. Do you have any source of what is an “unsuccessful recomposition”? Maybe a link I could read more to understand the implications of it? I’m not finding much around the concept… 🤔

Casey Brooks

12/16/2021, 9:42 PM
I really don't know exactly what it means either 😅 But the function's kdocs do reiterate that point of being on successful recomposition, and also noting exactly when it's called in relation to other effects, which would otherwise be undefined if just called from the function itself
👍 1

Marcello Galhardo

12/16/2021, 9:56 PM
Looks like you are right - it is called when the “composition” is completed basically, while the option 2 would be called in the middle of the composition from what I understood. Thank you @Casey Brooks for the references. Looks like what I’m looking for is something like:
DisposableEffect(viewModel) {
        // do something
        onDispose {  }
It would be really nice if
would support keys too so I don’t need to use
empty for these cases. 😞

Sean McQuillan [G]

12/16/2021, 10:09 PM
Yes this is correct. A composition may start and be abandoned, at which point any side effects should not happen
👍 2
SideEffect doesn't support keys as an intentional design btw
Early on we found that many people (including me) reached for it to build composition execution state machines which turned out to be super confusing 😂
😂 2

Marcello Galhardo

12/16/2021, 10:13 PM
Oh, that is very interesting, I would never have guessed that - thank you for sharing the details on the design decision @Sean McQuillan [G].

Zach Klippenstein (he/him) [MOD]

12/16/2021, 10:14 PM
An easy example of a composition being abandoned is this:
@Composable fun Thing() {
  SideEffect {

  throw RuntimeException("oopsie")
would never get called, but
would, even though the composition would never be applied, none of its UI would actually get emitted, and no state changes it performed would take effect.
🤔 1
There are other use cases that don’t involve throwing exceptions too


12/17/2021, 9:08 AM
So what's the primary use case for SideEffect? I can only put in code that is idempotent, because it will be called on every successful composition. I could put the same code in without using SideEffect and the behaviour stays the same, as my code must already handle multiple executions correctly. As far as i see it's only a minor optimization as my code get's skipped sometimes.


12/17/2021, 11:41 AM
Seems also good to demarcate things that affect external state, and split them out from the composition flow. I don't know if the compose runtime does anything special, but marking this seems valuable. Also it seems like a good thing to minimise the work of the composition.
👍 1