https://kotlinlang.org logo
#android-architecture
Title
# android-architecture
u

ursus

07/20/2019, 4:33 AM
that feels kind of weird; should I maybe map it? i.e.
State(val message: List<Message>, ...)
->
ExternalState(val items: List<Item>,...)
m

Menil Vukovic

07/24/2019, 8:36 PM
maybe I'm misunderstanding your problem, by why not map it to
List<Item>
in the first place? when you have a state that holds your initial list of items (of whatever kind) you can map them to your recyclerview and use the following pages to do the same. Last state you emitted will contain the last page of data, and the adapter of your recyclerview will contain all of items you have so far.
u

ursus

07/24/2019, 8:40 PM
because I list of Items will be heterogenous in such that it contains Message, LoadingItem, ErrorItem for example, or maybe Headers too etc
its impractical to insert the nnew page before last item etc, rather than just plain append to lsit of Messages
also my pagination works that last message' id is the parameter to the pagination, so its easiel to message.get(lastIndex)
m

Menil Vukovic

07/24/2019, 8:42 PM
ahh I see now. well why not just emit a new state, eg.
PageState(val pageNr: Int, val items: List<Item>)
during each pagination
u

ursus

07/24/2019, 8:42 PM
well, yes, thats my 2 options, either has both messages and items in State, or map State(messages) to ExternalState(items)
m

Menil Vukovic

07/24/2019, 8:42 PM
I assume if this is a slow operation (db, networking etc.) you could have a
LoadingPageState
which could trigger loading on the UI
u

ursus

07/24/2019, 8:43 PM
yes thats basically the question, wether its common in MVI to "format" the State to some kind of external consumed State', in order to hide internal stuff
m

Menil Vukovic

07/24/2019, 8:44 PM
I'm no expert on MVI but recently I'm using it in most of my projects, so my experience is the following: - more state means more code BUT - more state means more understanding
u

ursus

07/24/2019, 8:44 PM
yes explicit state is good but also hiding internals from public api is good
but I also unconsciously worry about the amount of objects instantiated..idk
m

Menil Vukovic

07/24/2019, 8:46 PM
I work on a heavy modified MVI that is full reactive and everytime i have a dillema like you have now I think:
what does my View need?
and I expose only things that my view will consume directly My
presenter
can take care of boxing, mapping or whatever is needed. I will only push pure usable data to my view
and frankly whichever approach you take I don't think it will better or worse than the other one
u

ursus

07/24/2019, 8:47 PM
yea, but I dont remember redux people remapping State to public State'
m

Menil Vukovic

07/24/2019, 8:48 PM
true, but that's why I have this modified version of MVI. I always push only the what we call "full view state" to the UI. full view state is a copy of the previous state + changes from the next (read: current) state
u

ursus

07/24/2019, 8:49 PM
I dont think pushing explicitly previosu state is good, however it depends on your view lifecycle
i.e. viewshould cache the last state it seen, not viewmodel
m

Menil Vukovic

07/24/2019, 8:49 PM
I don't have a viewmodel 😄
u

ursus

07/24/2019, 8:50 PM
or presenter whatever, .. anytime view scope is narrower than viewmdodel/presenter
m

Menil Vukovic

07/24/2019, 8:50 PM
let me show you a snippet of the a fullviewstate and the viewstate reducer. maybe you'll get inspired
Copy code
sealed class EmailLoginViewState

data class EmailLoginFullViewState(val error: ErrorMessageEnum? = null,
                                   val isValid: Boolean = false,
                                   val loading: Boolean = false,
                                   val eye: Boolean? = null)

class EmailLoginErrorViewState(val error: ErrorMessageEnum?) : EmailLoginViewState()
class EmailLoginForgotViewState : EmailLoginViewState()
class EmailLoginBackViewState : EmailLoginViewState()
class EmailLoginSignInViewState : EmailLoginViewState()
class EmailValidateViewState(val isValid: Boolean) : EmailLoginViewState()
class EmailLoginLoadingViewState(val loading: Boolean) : EmailLoginViewState()
class EmailEyeViewState(val eye: Boolean?) : EmailLoginViewState()
Copy code
override fun viewStateReducer(
      previousState: EmailLoginFullViewState,
      changes: EmailLoginViewState
  ): EmailLoginFullViewState {
    return when (changes) {
      is EmailLoginErrorViewState   -> previousState.copy(error = changes.error, loading = false)
      is EmailLoginForgotViewState  -> previousState.copy()
      is EmailLoginBackViewState    -> previousState.copy()
      is EmailLoginSignInViewState  -> previousState.copy(loading = false)
      is EmailValidateViewState     -> previousState.copy(isValid = changes.isValid)
      is EmailLoginLoadingViewState -> previousState.copy(loading = changes.loading)
      is EmailEyeViewState          -> previousState.copy(eye = changes.eye)
    }
  }
this way if I can say "yes paginated data is a new state for me" I'll be happy to create a new state, fixate on the data it needs to push and reduce it
u

ursus

07/24/2019, 8:58 PM
yea, however I'd expect changes not to be ViewState but some kind of Actions
m

Menil Vukovic

07/24/2019, 9:01 PM
there is a difference in the way we do this. I see Intents as Actions, and viewstates as Reactions or changes, weather it's UI or navigation
u

ursus

07/24/2019, 9:04 PM
right but you create new State based on prevous State + input action, how can you derive new state only from previous state
m

Menil Vukovic

07/24/2019, 9:10 PM
i don't. check the reducer method: it's not only from the previous state, but also the changes that comes with the new state eg.
(loading = false)
as soon as loading is finished
10 Views