Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass parameters for loading data via action? #69

Closed
Bodo1981 opened this issue Feb 26, 2020 · 6 comments
Closed

Pass parameters for loading data via action? #69

Bodo1981 opened this issue Feb 26, 2020 · 6 comments

Comments

@Bodo1981
Copy link

Hi @sockeqwe,

first of all: great work!
I love FlowRedux and already played a little bit with this library.

But I am facing and problem:
I am dispatching a loading action to the statemachine and this loading actions contains the data for loading feeds (e.g. id for a specific article). i looked at your best practices but i don't know how to get the data out of my action.

my current implementation:

init {
  spec {
    inState<Loading> {
      onEnter(block = ::loadScreen)
    }
    inState<Content<Any>> {
      on<ActivityReloadAction<Ref>> { _, _, setState ->
        setState { Loading(refresh = true) }
      }
    }
    inState<Error> {
      on<ActivityReloadAction<Ref>> { _, _, setState ->
        setState { Loading(refresh = false) }
      }
    }
  }
}

suspend fun loadScreen(getState: GetState<ActivityViewState>, setState: SetState<ActivityViewState>) {
  // here i miss the action which contains the data to do the http request
}

Maybe you can tell me how i can get the action which triggered the state or how i can pass in paramters to load the data?

Thx
Bodo

@sockeqwe
Copy link
Collaborator

sockeqwe commented Feb 26, 2020

Hey Bodo,
happy to hear that you like it.
You would make this information part of your State ( and use GetState to retrieve the state)

In general it's a good rule of thumb to make all information needed to restore the full state part of your State (then you get state restoration almost for free, i.e. restoring loading state from Bundle would then trigger onEnter again and load screen).

I.e. I think you should have it as part of your Loading State:

data class Loading(
   val refresh : Boolean, 
   internal val ref : Ref  // make this private or internal so it is only accessible for your state machine, but not for your View
)


suspend fun loadScreen(getState: GetState<ActivityViewState>, setState: SetState<ActivityViewState>) {
   val state = getState() as LoadingState
   val ref = state.ref

   loadData(ref)

   ...
}

Does that help?

@Bodo1981
Copy link
Author

Hey Hannes,

thanks for the hint to put it in the state!

whats best to the data (e.g. load the feed with other parameters, e.g. to the elements sorted desc). i think i will do it like this:

init {
  spec {
    inState<Loading> {
      onEnter(block = ::loadScreen)
    }
    inState<Content<Any>> {
      on<ActivityReloadAction<Ref>> { _, _, setState ->
        setState { Loading(refresh = true, ref = newRef) }
      }
    }
    inState<Error> {
      on<ActivityReloadAction<Ref>> { _, _, setState ->
        setState { Loading(refresh = false, ref = newRef) }
      }
    }
  }
}

-> that means i will pass the new ref with the updated request data via the ActivityReloadAction to the Loading ViewState

@sockeqwe
Copy link
Collaborator

I'm sorry, I don't fully understand the relationship between other parameters, i.e. sort order, and Ref and ActivityReloadAction. Does ref contain that parameters (i.e. sort order parameters)?

@Bodo1981
Copy link
Author

Bodo1981 commented Mar 3, 2020

So for example the inital load will be done with the following request:
http://www.foo.com?sort=asc
-> Ref(sort = "asc")

When the user wants to sort the list desc he has to call the feed with another parameter, so i dispatch the ActivityReloadAction(ref = Ref(sort = "desc")
http://www.foo.com?sort=desc

inState<Content<Any>> {
    on<ActivityReloadAction<Ref>> { _, _, setState ->
        setState { Loading(refresh = true, ref = newRef) }
    }
}

so the action contains the ref with the new url parameters to load the feed

@sockeqwe
Copy link
Collaborator

sockeqwe commented Mar 5, 2020

Yes, you can do it like that.

One Anti-Pattern that you should avoid (not sure if it related to your example) is to not leak any state related information in via Actions. For example, let's do a simple pagination example:

data class NextPageAction(
   val nextPage : Int   // Don't do this!
) : Action


inState<Content> {
    on<NextPageAction> { action, _, setState ->
        setState { Loading(page = action.nextPage) }
    }
}

The "anti pattern" here is that essentially an Action can "override" completely the state of your state machine. For example at some point, most likely in your UI, you have to create a NextPageAction(nextPage = currentPage + 1) that then gets dispatched to the State machine. The problem is that now you have some business logic currentPage + 1 outside of your state machine (most likely in your UI). So if you have a bug here, i.e. NextPageAction(nextPage = currentPage + 10) then your state machine would suddenly skip 9 pages in between.

Instead the current page should be part of your State:

object NextPageAction : Action

sealed class State {
   abstract val page : Int  
   data class Content( override page : Int) : State
   data class Loading( override page : Int) : State
}

inState<Content> {
    on<NextPageAction> { action, getState, setState ->
        setState { Loading(page = getState().page + 1) } // Now state machine drives the state
    }
}

It sounds a little bit that Ref could lead you into this "antipattern". It might be still ok to do that as long as you are aware of the downsides (i.e. passing a wrong Ref in could lead the state machine into a totally unwanted state transition because that Ref is basically overruling the state machine internal business rules or jump to a state that you didnt want to actually).

Does that help?

@Bodo1981
Copy link
Author

Yes this was very helpful, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants