Going full-featured¶
BaseFeature¶
If the reduced functionality of ReducerFeature and ActorReducerFeature is not enough for your case, this base class is your go-to.
BaseFeature takes four generic parameters:
BaseFeature<Wish, Action, Effect, State>
The new one here compared to the simpler Features is the Action
.
Actions¶
Use-case:
- you need some kind of an "internal
Wish
" to execute, but you don't want to leak it through yourWish
sealed class, as it would make it publicly callable. - you want to be able to trigger these "internal
Wish
es"
Action
is a superset of Wish
in the form of:
sealed class Wish {
object PublicWish1 : Wish()
object PublicWish2 : Wish()
object PublicWish3 : Wish()
}
sealed class Action {
data class Execute(val wish: Wish) : Action()
object InvalidateCache : Action()
object ReloadSomething : Action()
}
This has two implications:
-
For
BaseFeature
to know how your publicWish
maps to anAction
, you need to supply a mapping function in the constructortypealias WishToAction<Wish, Action> = (Wish) -> Action
-
Now your
Actor
will be acting uponAction
instead ofWish
So any incoming Wish
is mapped to an Action
, and executed in the Actor
along with all other Action
s:
class MyComplexFeature : BaseFeature<Wish, Action, Effect, State>(
// ...remainder omitted...
wishToAction = { Execute(it) },
actor = ActorImpl(),
) {
// ...remainder omitted...
sealed class Wish {
object PublicWish1 : Wish()
object PublicWish2 : Wish()
object PublicWish3 : Wish()
}
sealed class Action {
data class Execute(val wish: Wish) : Action()
object InvalidateCache : Action()
object ReloadSomething : Action()
}
class ActorImpl : Actor<State, Action, Effect> {
override fun invoke(state: State, action: Action): Observable<Effect> = when (action) {
is Execute -> when (action.wish) {
PublicWish1 -> TODO()
PublicWish2 -> TODO()
PublicWish3 -> TODO()
}
InvalidateCache -> TODO()
ReloadSomething -> TODO()
}
}
// ...remainder omitted...
}
So now you can have internal Action
s, but how will you trigger them? Meet the PostProcessor
.
PostProcessor¶
The PostProcessor
(as the name implies) will have a chance to react after a certain Action
was mapped to a certain Effect
which was used to create a new State
. At this point, it can signal the need for additional Action
s:
typealias PostProcessor<Action, Effect, State> = (Action, Effect, State) -> Action?
Using the example above this could be:
class MyComplexFeature : BaseFeature<Wish, Action, Effect, State>(
// ...remainder omitted...
postProcessor = PostProcessorImpl()
) {
// ...remainder omitted...
class PostProcessorImpl : PostProcessor<Action, Effect, State> {
// do anything based on action (contains wish), effect, state
override fun invoke(action: Action, effect: Effect, state: State): Action? {
if (state.i == 101) {
return InvalidateCache
}
return null
}
}
}
The implementation of BaseFeature
wires everything up for you from mapping your Wish
to Action
, calling your Actor
, Reducer
, and PostProcessor
and emitting the next State
.