https://kotlinlang.org logo
n

Noé Casas

01/20/2021, 7:35 AM
Is it Ok to use non-local state from a Composable function? An example would be a Composable that shows a Text with a String taken from a 
MutableState
 stored as a member of an object retrieved through an Ambient, like this:
Copy code
data class ServiceX (
   val whateverString: MutableState<String>("meow")
)


@Composable
fun Whatever() {
   val serviceX = AmbientServiceX.current
   Text(serviceX.whateverString)
}
Will the Composable function repaint when 
whateverString
 changes? Are there any problems with this?
j

jim

01/20/2021, 10:14 AM
If someone is using ambients, I am 99% sure they are abusing them. Think of ambients like globals. Sometimes they are useful/necessary, but you should feel guilty every time you use them.
👍 3
Specifically, Ambients (like globals) create implicit dependencies which makes it hard to track the flow of information through larger codebases.
n

Noé Casas

01/20/2021, 1:46 PM
In this case the ambient is to distribute the instance of a service that keeps as a member variable the user that is logged in, together with other info, similar to this: https://foso.github.io/Jetpack-Compose-Playground/general/ambient/#how-to-use-a-value-of-an-ambient . Is this use of the ambient fair? Would it work properly with the Composable?
a

Adam Powell

01/20/2021, 3:00 PM
It will work from a mechanical perspective, and yes, your UI will invalidate and recompose as expected when that snapshot state object changes. But as Jim pointed out, there are other reasons you might not want to structure your code that way for understandability and maintainability.
In addition to ambients being implicit dependencies that add complications to both usage and testability, you probably don't want to pass raw MutableState objects to a constructor. That implies a kind of complicated ownership model for that state; what is it shared with? Who has access to mutate it and under what circumstances?
n

Noé Casas

01/20/2021, 3:16 PM
The state contains information about the logged user, including permissions they have, so any Composable that needs permission checking is a candidate for checking it. Also, top level Composables check if the state is null to show a login form or the normal UI. The state is only mutated when logging in, logging out or async refreshing of the user info.
a

Adam Powell

01/20/2021, 3:53 PM
generally you would want to write
ServiceX
as something more like:
Copy code
class ServiceX(whateverString: String) {
  var whateverString by mutableStateOf(whateverString)
    private set // optional, but often useful!
  // ...
}
with the key distinction being keeping the
MutableState
object as an implementation detail. Then all of the usual OO patterns apply, the only difference is that changes to `ServiceX`'s internal state are observable.
n

Noé Casas

01/20/2021, 3:55 PM
I see. With that structure I understand that modifications to
whateverString
by means of a public function of
ServiceX
would trigger the repaint of Composables that use
whateverString
(e.g.
Text(serviceX.whateverString)
), right?
a

Adam Powell

01/20/2021, 3:57 PM
yes. Observation is transitive across the call stack; even if the actual snapshot state object is many objects or function calls away, compose still knows it was accessed.
n

Noé Casas

01/20/2021, 3:57 PM
Perfect, thanks a lot!
👍 1
2 Views