does anyone have examples of architecture using co...
# compose
v
does anyone have examples of architecture using compose? My entire app is using databinding + presenters (BaseObservable).. but now I change it to use viewmodels + livedata (compose views only) .. my old presenters describe all formatted values in variables.. and bind it to views.. now my composables use the data object to populate the view.. should I build a presenter/helper to format values for composables? how can I unit test it? Is it strange to use viewmodels for screen state and presenters for data formatting?
t
@Vitor Prado might be worth checking out the VIPER architecture used in iOS; the data from network/persistence can be formatted in the "Interactor". Screen state can still be managed by the ViewModel in Android's case, which can grab formatted data from the Interactor.
v
thanks @Tash!
👍🏼 1
s
If you’re familiar with Redux, I highly recommend it for a Compose app. Keep a state tree in your store, update with actions and reducers, and then connect any composable component to any part of your state tree, with live updates anytime the subscribed portion of the state changes. It looks like this:
Copy code
val communities by remember { store.mutableSubscribedStateOf { it.communities } }
v
@Sam I like de redux idea .. but I think confuse when app gets bigger.. should I keep the entire app state on my store? or should I make a store for each screen/component?
s
our app is 155,000 lines of code, and redux scaled very well
v
do you have an example? all the examples in internet is about a single screen app.
s
the state tree is a single store, but the way you interact with it is very much in separate discreet pieces:
as an app gets bigger, the lines become blurred as to what screen owns which part of your state, and that’s the beauty of this approach
you store state in your tree according to how the state should be organized. it’s only stored once, so you have a very clean source of truth
your components are interested in a few things: • getting portions of the state required to render the UI, and subscribing to updates to re-render anytime that portion of data changes • [optionally] transforming the state to a structure can be better consumed (e.g. store has a map of objects, your
LazyColumn
needs a list) • updating the state tree with
Actions
when user performs interactions (which looks like
store.dispatch(AccountActions.UpdateUsername(usernameTextFieldState.text))
v
how about pagination? is it a problem to hold a lot of values in memory?
appstate should be immutable, should’t? if you use
val
instead of
var
makes it harder to deal with state change?
s
yes each part of the state tree is immutable, so when you execute the reducer (the place where the data from an
Action
gets reconciled into the existing state to form new state) you will build a new version of the state. The reducers are broken up into manageable chunks too, and kotlin+data classes make the immutable stuff pretty straightforward
here’s an example of handling the data in each action
v
@Sam Even so, the renderization is based on data models.. the main question in my head about my current approach is about using a “presentation” to deal with value formmating (and be testable) heres a very simple example: I have to show the correct button based in a boolean
Copy code
data class User(
   val admin: Boolean
)
Copy code
if (user.admin)
   Button() // button for admins, open "edit" screen
else
   Button() // button for users, open "show" screen
this is a really simple example.. but think about real complex UIs with a lot of validations.. it looks bad and adds complexity to de “view” layer.. and if i delegate it to my viewmodel it looks strage, ’cause my viewmodel deal with “view state” (loading / error / data) .. and this piece of view is rendered when data is loaded, looks like a mixed scenario with async data (view state) and a static data (the data loaded).. and how about testing?! looks like hell
s
In your scenario, the approach differs depending on the quantity of conditions/validation on your screen. For instance, if the screen is highly geared towards admin, create many smaller stateless [dumb] components and have a dedicated
AdminScreen
. Conversely, if the intention is a user screen with a little bit of admin functionality sprinkled in, then using conditional rendering based on
user.admin
doesn’t look bad to me. There are other ways of distributing the logic, depending on your taste:
Copy code
fun onPressStart() {
        var action = if (user.admin) AccountActions.startAdmin() else AccountActions.startUser()
        store.dispatch(action)
    }

    Column {
        Button(::onPressStart) { Text(if (user.admin) "Start as Admin" else "Start") }
    }