What is the correct way to handle Events which are...
# compose
s
What is the correct way to handle Events which are coming from ViewModel, i was trying to follow Official guideline. Launch Effect (ViewState) { If ((ViewState.someBoolean) { Somefunc() } } If ((ViewState.someBoolean) { Launch Effect (true) { Somefunc() } }
c
Have a look here https://developer.android.com/jetpack/compose/state#viewmodels-source-of-truth You are not following the guidelines 🙂
a
State and event are different things. Using a state to represent events is wrong at the first place. You should expose events as a
Flow
and collect the flow in a
LaunchedEffect
.
c
But the ViewModel should consume the evens and expose the state otherwise it's not a "view model".
a
No. There are cases where exposing an event flow to UI layer is reasonable. For example error messages for which you show snackbars.
c
why? the error is a "state" triggered from an "event" that should be handled by the view model.
If you always follow this pattern, maintaining your app and testing it is much easier 😉
a
Does your app always show the error state or it just show a one-time exclamation on error? In the latter case, errors are events instead of a state.
Anyway what the op is doing is basically triggering a one-shot action when a "state" becomes true, which is obviously not what a state is intended to be used.
👍 1
c
For the one shot action @Albert Chang https://developer.android.com/jetpack/guide/ui-layer/events#handle-viewmodel-events doesn't this contradict your statement? "This UI reacts to changes to the 
isUserLoggedIn
 state and navigates to the correct destination as needed:" I'm still learning, so I'm probably wrong. @Chrimaeon if you want, the docs were recently updated to have a case on snackbars. https://developer.android.com/jetpack/guide/ui-layer/events#consuming-trigger-updates
c
As seen in the example the trigger is outside the composable scope, so can haben everywhere. Then in the viewmodel the state is updated and the state is then consumed by the Composable.
a
The view model notifys the UI with a state, the UI shows a snackbar and when the snackbar disappears (without user interaction) the UI notifys the view model, the view model generate a new state. This flow seems somewhat strange and overly complicated to me. At least IMO using an event flow is much clearer with less code.
👍 2
c
yeah. its similar to this thread here from a while back involving events: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1640618092111200?thread_ts=1627406028.253800&cid=CJLTWPH7S
😂 1
seems like people are torn on it. im just gonig to follow the recs about it, because i dont want to think about these sorts of things. i rather just accept it and focus on building my business
👍 2
a
there's no reason why a ViewModel can't be a holder of other objects that implement this kind of state reducing. SnackbarHostState is a good example of this (for exactly this example too!) https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/[…]compose/material/SnackbarHost.kt;l=60
This class's whole job is to take events (callers of the
showSnackbar
method) and reduce those events to state (
currentSnackbarData
). The methods on the
SnackbarData
interface are also events: events published by the UI itself both when the user interacts with the snackbar and when the UI determines that it has shown a particular message for a timeout period
The
SnackbarHostData
has no UI dependencies, so it's safe to keep one in a
ViewModel
or even a global singleton if you feel like it. Composables on any screen in your app can present this using a
SnackbarHost
or your own implementation, since
SnackbarHost
only uses the public API of
SnackbarHostState
. You can test anything that your code does to a
SnackbarHostState
without involving composition or UI at all.
Specifically, many screens in your app can present the same snackbar data from the same
SnackbarHostState
instance without worrying about losing events or which UI consumed the event that a particular snackbar notification is talking about
a
I think the biggest consideration is process death. If you have events that need to live on, go for the UiState approach and save state library. If you don't, a one shot single consumption event stream is good enough. Personally I've never really had to deal with process death, so for me I do the event stream