使用RxJava构建MVI循环:如何使用scan()

时间:2018-03-27 10:47:30

标签: kotlin rx-java2 behaviorsubject

我试图想办法消除可变状态,从而消除可能的竞争条件。但我似乎无法弄清楚如何以某种方式和#34;交织在一起"两个Observable,同时也使用" scan"。

希望通过显示更多代码,我可以给你一个想法:

private val stateRelay: BehaviorRelay<State> = BehaviorRelay.createDefault(initialState ?: DEFAULT_STATE) // maybe this should be `Observable.startWith()` somehow?

fun bindIntents(intents: Observable<Actions>, stateRenderer: StateRenderer) {
    compositeDisposable += intents.concatMap { action ->
        when (action) {
            is Actions.Increment -> {
                Observable.create<Change> { emitter -> 
                   // emit things
                }
            }
            is Actions.Decrement -> {
                Observable.create<Change> { emitter -> 
                   // emit things
                }
            }
        }
    }.map { change ->
        reducer(stateRelay.value, change) // TODO: figure out how to use scan() here, instead of stateRelay.value! :(
    }.subscribeBy { newState ->
        stateRelay.accept(newState) // there is a chance that the relay shouldn't be here if scan is used
    }

    compositeDisposable += 
        stateRelay // TODO: figure out how to use scan() instead of a relay!
            .distinctUntilChanged()
            .subscribeBy { state ->
                stateRenderer(state)
            }
}

fun unbindIntents() {
    compositeDisposable.clear()
}

所以我在这个方法中收到了Observable<Actions>,从技术上来说,另一方面是PublishRelay(这应该没问题)。

然而,不知怎的,我应该用BehaviorRelay替换Observable.scan()(可能用startWith)来消除可变状态,但我似乎无法把我的头围绕着我应该为此发生的事情。

至于涉及的类型,如果需要:

private typealias Reducer = (state: State, change: Change) -> State

private typealias StateRenderer = (state: State) -> Unit

@Parcelize
data class State(val count: Int): Parcelable

我如何将intents.concatMap.map作为Observable.scan()(可能startWith()replay(1))的一部分来包装,以消除我对BehaviorSubject的使用?

1 个答案:

答案 0 :(得分:1)

我将详细阐述上述评论。 这是对您的代码的简单重写,以满足您的要求。

fun bindIntents(intents: Observable<Actions>, stateRenderer: StateRenderer) {
    val stateObservable = intents.concatMap { action ->
        when (action) {
            is Actions.Increment -> {
                Observable.create<Change> { emitter ->
                // emit things
                }
            }
            is Actions.Decrement -> {
                Observable.create<Change> { emitter ->
                    // emit things
                }
            }
        }
    }.scan(initialState, { currentState, change -> reducer(currentState, change)})

    compositeDisposable +=
        stateObservable
                .distinctUntilChanged()
                .subscribeBy { state ->
                    stateRenderer(state)
                }
}

请注意,可以通过内联我在下面的表达式中分配给stateObservable的可观察对象并使用方法引用作为第二个参数进行扫描来进一步简化

fun bindIntents(intents: Observable<Actions>, stateRenderer: StateRenderer) {
    compositeDisposable +=
            intents.concatMap { action ->
                when (action) {
                    is Actions.Increment -> {
                        Observable.create<Change> { emitter ->
                            // emit things
                        }
                    }
                    is Actions.Decrement -> {
                        Observable.create<Change> { emitter ->
                            // emit things
                        }
                    }
                }
            }.scan(initialState, this::reducer)
                    .distinctUntilChanged()
                    .subscribeBy { state ->
                        stateRenderer(state)
                    }
}