I recently tried to explain some of the APIs that ...
# compose
z
I recently tried to explain some of the APIs that compose uses to make its state management work. It’s all based on something called “snapshots”. You probably won’t ever need to actually use these they’re a bit lower level, but knowing how this works might help you reason about how your code is running. Read more here.
👀 3
👍 22
👍🏼 4
c
I will say that this also helped me further understand why mutableStateOf isn't some kind of super terrible thing I should be using. As someone that used Epoxy a ton... it was drilled into my head that mutable objects are BAD. And now, coming to compose I was jumping through a lot of hoops trying to use LiveData with immutable data and then converting that to mutableStateOf. Thanks again Zach
👍🏻 1
z
Yea I think we’re all so used to being terrified of mutable state after rxjava that this is going to: 1. take some time to adjust to 2. open up a new subtree on the architecture decision tree that we’ve cut off so long ago we forgot it existed. And maybe even spawn new patterns we haven’t even thought of yet.
☝️ 1
e
I felt uncomfortable enough with it that I make sure all of my state is `@Immutable`(either explicitly or implicitly from the compiler) and use
Flow.collectAsState
at the top of my tree, and rely on Compose skipping nodes if the state didn't change. Haven't done enough real work with it to see if there are performance problems at scale, but so far so good.
c
Yeah, I think (if I understood Adam Powell correctly) using collect as state when you could just use snapshot state directly is a little wasteful as collectAsState has to in turn use snapshot state anyway. I doubt that it's anything you'd be able to notice but I guess it's worth pointing out.
z
I don’t think it’s wasteful from a performance perspective, but maybe conceptually, if your architecture isn’t already based around publishing streams of value types or you’re trying to deal with aggregating lots of complex state into a single “view” of it. In that case, using finer grained mutable snapshot state might simplify things. But as usual with architecture decisions, the important thing is that you have an architecture pattern you’re following at least loosely. Compose just gives you some more flexibility.
e
I guess it depends on whether that's considered "idiomatic" because @Leland Richardson [G] mentioned in ADBC (maybe #165?) that the team is optimizing for use cases where users are writing idiomatic Kotlin. So while I do like my stream based architecture, if its performance won't scale well I'd rather switch to some other architecture that will.
z
I think stream-based architectures are more likely to run into complexity issues long before performance ones. I work on a huge app that used to use rxjava as an architectural backbone and we never hit performance problems but we definitely hit complexity ones. The best part of compose’s state management isn’t that it has some huge performance wins (again, streams are perfectly fast) but that it lets you write simpler code, which is easier to maintain etc.
e
I'm worried more about any performance implications at the compose layer due to
collectAsState
at the root, and passing the different values down the tree. Streams are actually more of a implementation detail in my architecture (https://github.com/eygraber/cure; still in the planning phase and not production ready yet). The basic idea is:
Many UseCases (business logic, etc...) -> Compositor (combines all of the use cases data into a State object and emits to a flow) -> Renderer (collectAsState from the Compositor and translate the State into UI)
There's some building blocks around that which make it easier to work with, but that's the main idea.