diff options
2 files changed, 71 insertions, 9 deletions
diff --git a/kvision-modules/kvision-redux/src/main/kotlin/pl/treksoft/kvision/redux/StateBinding.kt b/kvision-modules/kvision-redux/src/main/kotlin/pl/treksoft/kvision/redux/StateBinding.kt index 55fcdb34..383e734e 100644 --- a/kvision-modules/kvision-redux/src/main/kotlin/pl/treksoft/kvision/redux/StateBinding.kt +++ b/kvision-modules/kvision-redux/src/main/kotlin/pl/treksoft/kvision/redux/StateBinding.kt @@ -36,30 +36,42 @@ import redux.RAction * @param container a container * @param factory a function which re-creates the view based on the given state */ -class StateBinding<S : Any, A : RAction, CONT : Container>( +class StateBinding<S : Any, A : RAction, CONT : Container, CONTENT>( store: ReduxStore<S, A>, private val container: CONT, - private val factory: (CONT.(S) -> Unit) + private val factory: (CONT.(S) -> CONTENT) ) : Widget(setOf()) { init { - container.add(this) - container.factory(store.getState()) + update(store.getState()) store.subscribe { update(it) } } + private var updateState: ((S, CONTENT) -> Unit)? = null + private var content: CONTENT? = null + /** * Updates view from the current state. */ @Suppress("ComplexMethod") fun update(state: S) { singleRender { - container.removeAll() - container.add(this) - container.factory(state) + if (updateState == null || content == null) { + container.removeAll() + container.add(this) + content = container.factory(state) + } else { + content?.let { + updateState?.invoke(state, it) + } + } } } + private fun setUpdateState(updateState: (S, CONTENT) -> Unit) { + this.updateState = updateState + } + companion object { /** * DSL builder extension function. @@ -69,8 +81,29 @@ class StateBinding<S : Any, A : RAction, CONT : Container>( fun <S : Any, A : RAction, CONT : Container> CONT.stateBinding( store: ReduxStore<S, A>, factory: (CONT.(S) -> Unit) - ): StateBinding<S, A, CONT> { + ): StateBinding<S, A, CONT, Unit> { return StateBinding(store, this, factory) } + + /** + * DSL builder extension function for updateable redux content. + * + * It takes the same parameters as the constructor of the built component. + */ + fun <S : Any, A : RAction, CONT : Container, CONTENT> CONT.stateUpdate( + store: ReduxStore<S, A>, + factory: (CONT.(S) -> CONTENT) + ): Updateable<S, CONTENT> { + return Updateable(StateBinding(store, this, factory)::setUpdateState) + } + } +} + +/** + * A helper class for updateable redux content. + */ +class Updateable<S : Any, CONTENT>(private val setUpdateState: ((S, CONTENT) -> Unit) -> Unit) { + infix fun updateWith(updateState: (S, CONTENT) -> Unit) { + setUpdateState(updateState) } } diff --git a/kvision-modules/kvision-redux/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt b/kvision-modules/kvision-redux/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt index 1da16e55..0271a956 100644 --- a/kvision-modules/kvision-redux/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt +++ b/kvision-modules/kvision-redux/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt @@ -26,6 +26,7 @@ import pl.treksoft.kvision.html.Div.Companion.div import pl.treksoft.kvision.panel.Root import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.redux.StateBinding.Companion.stateBinding +import pl.treksoft.kvision.redux.StateBinding.Companion.stateUpdate import pl.treksoft.kvision.redux.createReduxStore import redux.RAction import test.pl.treksoft.kvision.DomSpec @@ -77,4 +78,32 @@ class StateBindingSpec : DomSpec { } } -}
\ No newline at end of file + @Test + fun stateUpdate() { + run { + val root = Root("test", true) + val store = createReduxStore(::stateReducer, State(10)) + + val container = SimplePanel() + container.stateUpdate(store) { state -> + div("${state.counter}") + } updateWith { state, d -> + d.content = "${state.counter}" + } + root.add(container) + val element = document.getElementById("test") + assertEqualsHtml( + "<div><div></div><div>10</div></div>", + element?.innerHTML, + "Should render initial state of the container" + ) + store.dispatch(StateAction.Inc) + assertEqualsHtml( + "<div><div></div><div>11</div></div>", + element?.innerHTML, + "Should render changed state of the container" + ) + } + } + +} |