Reading the <docs> from `SideEffect` it says: &gt;...
# compose
m
Reading the docs from
SideEffect
it says:
To share Compose state with objects not managed by compose, use the 
SideEffect
 composable, as it’s invoked on every successful recomposition.
If a
SideEffect
is called on “each recomposition”, what is the difference between the two codes:
Copy code
@Composable
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
SideEffect
would not be triggered if a recomposition happens (similar to
LaunchedEffect
and how it works with the keys) but based in the docs I think I’m missing something…
c
I'm not 100% sure, but my guess is the important thing to note with using the
SideEffect
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
SideEffect
doesn't get called, helping to avoid leaving your application in a bad state.
m
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… 🤔
c
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 https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#SideEffect(kotlin.Function0)
👍 1
m
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:
Copy code
DisposableEffect(viewModel) {
        // do something
        onDispose {  }
    }
It would be really nice if
SideEffect
would support keys too so I don’t need to use
onDispose
empty for these cases. 😞
s
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
m
Oh, that is very interesting, I would never have guessed that - thank you for sharing the details on the design decision @Sean McQuillan [G].
z
An easy example of a composition being abandoned is this:
Copy code
@Composable fun Thing() {
  SideEffect {
    doThingSafely()
  }
  doThingUnsafely()

  throw RuntimeException("oopsie")
}
doThingSafely
would never get called, but
doThingUnsafely
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
j
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.
y
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