Does `@Compose` support side effects, like launchi...
# compose
z
Does
@Compose
support side effects, like launching a dialog or a
Snackbar
that auto-dismisses itself after a short time?
obviously this is a bit of an anti-pattern but I’m curious how this would be supported
s
It is posible to express side effects. A good example of this would be the entrance/exit animation for a snackbar. There's two main APIs you should look at: onCommit and launchInComposition. onCommit – create a new lifecycle for the keys passed, allowing you to trigger an idempotent side effect when the keys change and cleanup from it in it's onDestroy block
Copy code
onCommit(snackBarShown) {
    // animate it
    onDispose {
       // the keys changed, or the containing composable has been removed
    }
}
launchInComposition – launch a coroutine (this is the preferred way to express fixed intervals like 1_000ms, do other coroutines type things)
Copy code
launchInComposition(snackbarShown) {
   // launch a new coroutine when snackbarShown changes
}
Making side effects inside of composition (in the main body of a composable function or lambda) is not a supported use case. The recomposer will often recompose composables either more often or more rarely than expected. The recomposer may also cancel a recomposition pass in some cases - and it can't undo side effects visible outside of composition.
👍 1
z
Making side effects inside of composition (in the main body of a composable function or lambda) is not a supported use case.
Oh for sure. I would just be consuming from a regular
Channel()
to async display a UI component. But do these side effect UIs persist/restore themselves across config changes?
s
What do you mean?
z
like if you launch a
DialogFragment
then do a config change, the
DialogFragment
is still visible because the
FragmentManager
handles restoring its state
s
Ah – so the execution units defined by onCommit and launchInComposition are tied to their calling composable being part of the tree. During rotation currently, the tree will be rebuilt and these execution units will be restarted.
onCommit – onDispose will be called for previous call, and then if the same component is added after rotation the onCommit block will start launchInComposition – the coroutine will be killed when the calling component is removed and a new one started in the new composable
In the future, the tree may survive rotation in some cases but even then this will happen in some situations where process death is unavoidable (e.g. long call)
State that should survive process death should be use
savedInstanceState
or
rememberSavedInstanceState
or be hoisted out of compose to e.g. Room
One rule that I think of to understand using onCommit: onCommit + onDispose should create an idempotent action. In theory it should be able to be commited and disposed every frame without adverse side effects. I'm still getting a feel for practical launchInComposition usage, but it similarly should prefer idempotent actions (e.g. if it calls a non-idempotent API and gets restarted it may throw on second call)
b
@zak.taccardi did you ever look into the auto-dismiss side effect further?
I whipped up this (https://gist.github.com/bmc08gt/0869fd75f493114f88fefd8b7a69237e) before I came upon this thread; uses launchInComposition