badoo / MVICore

MVI framework with events, time-travel, and more
https://badoo.github.io/MVICore/
Other
1.27k stars 90 forks source link

Private state representation for feature #119

Open tieskedh opened 4 years ago

tieskedh commented 4 years ago

I have a NavFeature which handles routes. NavWish(path: String) goes in and NavState(module: String, args : ...) goes out. All my code depends on Store<NavWish, NavState>. Now I want to use a stack with NavStates, but I don't want the rest of my code to know that I'm using a Stack.

What is the best way to expose a Store-interface without the stack, but use the stack interrnally?

ShikaSD commented 4 years ago

Hi @tieskedh Currently, feature does not support private states, but you can hide it using binder.


// state in feature
data class State(
    val stack: List<String>
)

// somewhere outside
binder.bind(feature to navigator using StateToNavigator)

object StateToNavigator : (State) -> NavState {
    override fun invoke(state: State): NavState = NavState(state.stack.last())
}
tieskedh commented 4 years ago

But in that case, the class passed around doesn't implement Store anymore... At this moment I'm using the following code:

class PublicNavigationFeature : Store<NavigationWish, NavigationState>{
    private val subject  = BehaviorSubject.create<NavigationStackState>()
    val navigationStackFeature = NavigationStackFeature().apply {
        subscribe(subject)
    }.wrapWithMiddleware()

    override val state: NavigationState get() = subject.value!!.navigationState.last()

    override fun accept(wish: NavigationWish) = navigationStackFeature.accept(wish)

    override fun subscribe(observer: Observer<in NavigationState>) {
        subject.map { 
            it.navigationState.lastOrNull() ?: NavigationState("close", "close") 
        }.subscribe(observer)
    }
}
ShikaSD commented 4 years ago

If you subscribe to state update directly, it is possible to use abstraction similar to yours (although it is better to call it PublicNavigationStore as it is not a feature anymore).

We encourage using Binder nonetheless, as it will take care of lifecycle management, so nothing will be leaked. You can pass NavigationStackFeature around (it implements store) and use transformer to access required fields. As the state is not mutable anyway, you won't be able to update it from outside, so the stack data will be consistent.