that feels kind of weird; should I maybe map it? i...
# android-architecture
u
that feels kind of weird; should I maybe map it? i.e.
State(val message: List<Message>, ...)
->
ExternalState(val items: List<Item>,...)
m
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
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
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
well, yes, thats my 2 options, either has both messages and items in State, or map State(messages) to ExternalState(items)
m
I assume if this is a slow operation (db, networking etc.) you could have a
LoadingPageState
which could trigger loading on the UI
u
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
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
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
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
yea, but I dont remember redux people remapping State to public State'
m
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
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
I don't have a viewmodel 😄
u
or presenter whatever, .. anytime view scope is narrower than viewmdodel/presenter
m
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
yea, however I'd expect changes not to be ViewState but some kind of Actions
m
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
right but you create new State based on prevous State + input action, how can you derive new state only from previous state
m
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