Hi all! I’m wondering if there’s any discussion or...
# compose
a
Hi all! I’m wondering if there’s any discussion or guidance around having certain actions be conditioned on state, when that state may change. For an example, this snippet is an excerpt from
SignInScreen
in
Jetsurvey
. The sign-in button is only enabled when the
emailState
and
passwordState
are deemed valid, which works most of the time. However, suppose
email
is valid, so the button is enabled. If the
email
is updated to become invalid just before the sign-in button is tapped (but within the same frame),
onSignInSubmitted
will be called with the invalid
email
, bypassing the validation.
c
You’d probably want to have your ViewModel or whatever actually checking the validation of the email, and accepting or rejecting
onSignInSubmitted
. And then the view itself just passively displays whether the email is valid or not, having it passed in from its function parameters. In general, lifting the state up into the “parent” composables and passing the relevant values in through the function parameters can help eliminate these kinds of subtle UI issues/race conditions. If you’re concerned about this kind of situation, it’s probably a good indicator that the state should be lifted up and passed in, rather than
remember [ }
-d directly in the function
1
a
Think of your UI as a client to your business logic's server. Your data model code should enforce internal consistency and correctness of its inputs. Don't trust the client. The client/UI may need to reflect the nature of why the attempted input is invalid back to the user, but it's not responsible for being the validation state machine that protects the rest of your system from bad inputs.
a
Just confirming my understanding with how that interacts with navigation: If some sort of navigation should occur as a result of that sign in button being tapped, the data model should return some sort of result for whether the sign in was successful. The UI to display loading or a failure to the user would be best be passed down through via normal states for composition, but within the
onClick
lambda itself the method to sign in should also indicate a failure so that the resulting navigation shouldn’t occur. Or perhaps the “navigation to run on success” is itself passed as a lambda to the data model?
a
ideally that shouldn't need a lambda passed anywhere, just plain sequential code;
Copy code
if (dataModel.submit(data)) {
  navigateTo(destination)
}
(and if that submit operation is async,
suspend fun
to the rescue)
⏸️ 2
a
I really like the model of thinking of the UI as a client to the business logic’s server. So the
suspend fun
could also implicitly handle any more complicated logic that the data model is doing. If the requirement was to cancel an ongoing request if the inputs changed (email, password), or prevent multiple simultaneous requests from the client UI, then the data model would be in control of its own state and whatever any of its functions return as it gets updates on what the user is doing.
a
yes, and then the UI becomes a reflection of the state of the data model and nothing more
🎉 1
there are lots of neat ways to control for concurrent access in that model as well; take a look at compose's
MutatorMutex
for one example
🤯 1
👀 1
a
Thanks for the helpful insight! That is super cool… I’ve played around with an
actor
pattern quite a bit for serially handling events in the data model (pre-Compose). It’s useful that
suspend fun
+ cancellation can support a lot of implementation behavior with a “common” contract.
a
yep! we use it all over for things like animations and input handling