fix state emissions becoming out of sync when using async handlers
This commit is contained in:
parent
72fa795d38
commit
e753cd30ad
|
@ -11,7 +11,6 @@ import app.dapk.state.Store
|
||||||
import app.dapk.state.createStore
|
import app.dapk.state.createStore
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class StateViewModel<S, E>(
|
class StateViewModel<S, E>(
|
||||||
reducerFactory: ReducerFactory<S>,
|
reducerFactory: ReducerFactory<S>,
|
||||||
|
@ -32,7 +31,7 @@ class StateViewModel<S, E>(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispatch(action: Action) {
|
override fun dispatch(action: Action) {
|
||||||
viewModelScope.launch { store.dispatch(action) }
|
store.dispatch(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,18 +4,26 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
fun <S> createStore(reducerFactory: ReducerFactory<S>, coroutineScope: CoroutineScope): Store<S> {
|
fun <S> createStore(reducerFactory: ReducerFactory<S>, coroutineScope: CoroutineScope, log: Boolean = false): Store<S> {
|
||||||
val subscribers = mutableListOf<(S) -> Unit>()
|
val subscribers = mutableListOf<(S) -> Unit>()
|
||||||
var state: S = reducerFactory.initialState()
|
var state: S = reducerFactory.initialState()
|
||||||
return object : Store<S> {
|
return object : Store<S> {
|
||||||
private val scope = createScope(coroutineScope, this)
|
private val scope = createScope(coroutineScope, this)
|
||||||
private val reducer = reducerFactory.create(scope)
|
private val reducer = reducerFactory.create(scope)
|
||||||
|
|
||||||
override suspend fun dispatch(action: Action) {
|
override fun dispatch(action: Action) {
|
||||||
scope.coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
|
if (log) println("??? dispatch $action")
|
||||||
state = reducer.reduce(action).also { nextState ->
|
state = reducer.reduce(action).also { nextState ->
|
||||||
if (nextState != state) {
|
if (nextState != state) {
|
||||||
|
if (log) {
|
||||||
|
println("??? action: $action result...")
|
||||||
|
println("??? current: $state")
|
||||||
|
println("??? next: $nextState")
|
||||||
|
}
|
||||||
subscribers.forEach { it.invoke(nextState) }
|
subscribers.forEach { it.invoke(nextState) }
|
||||||
|
} else {
|
||||||
|
if (log) println("??? action: $action skipped, no change")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +43,7 @@ interface ReducerFactory<S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun interface Reducer<S> {
|
fun interface Reducer<S> {
|
||||||
suspend fun reduce(action: Action): S
|
fun reduce(action: Action): S
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <S> createScope(coroutineScope: CoroutineScope, store: Store<S>) = object : ReducerScope<S> {
|
private fun <S> createScope(coroutineScope: CoroutineScope, store: Store<S>) = object : ReducerScope<S> {
|
||||||
|
@ -45,7 +53,7 @@ private fun <S> createScope(coroutineScope: CoroutineScope, store: Store<S>) = o
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Store<S> {
|
interface Store<S> {
|
||||||
suspend fun dispatch(action: Action)
|
fun dispatch(action: Action)
|
||||||
fun getState(): S
|
fun getState(): S
|
||||||
fun subscribe(subscriber: (S) -> Unit)
|
fun subscribe(subscriber: (S) -> Unit)
|
||||||
}
|
}
|
||||||
|
@ -82,14 +90,18 @@ fun <S> createReducer(
|
||||||
actionHandlers.fold(acc) { acc, handler ->
|
actionHandlers.fold(acc) { acc, handler ->
|
||||||
when (handler) {
|
when (handler) {
|
||||||
is ActionHandler.Async -> {
|
is ActionHandler.Async -> {
|
||||||
|
scope.coroutineScope.launch {
|
||||||
handler.handler.invoke(scope, action)
|
handler.handler.invoke(scope, action)
|
||||||
|
}
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
|
|
||||||
is ActionHandler.Sync -> handler.handler.invoke(action, acc)
|
is ActionHandler.Sync -> handler.handler.invoke(action, acc)
|
||||||
is ActionHandler.Delegate -> when (val next = handler.handler.invoke(scope, action)) {
|
is ActionHandler.Delegate -> when (val next = handler.handler.invoke(scope, action)) {
|
||||||
is ActionHandler.Async -> {
|
is ActionHandler.Async -> {
|
||||||
|
scope.coroutineScope.launch {
|
||||||
next.handler.invoke(scope, action)
|
next.handler.invoke(scope, action)
|
||||||
|
}
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ class ReducerTestScope<S, E>(
|
||||||
}
|
}
|
||||||
private val reducer: Reducer<S> = reducerFactory.create(reducerScope)
|
private val reducer: Reducer<S> = reducerFactory.create(reducerScope)
|
||||||
|
|
||||||
override suspend fun reduce(action: Action) = reducer.reduce(action).also {
|
override fun reduce(action: Action) = reducer.reduce(action).also {
|
||||||
capturedResult = it
|
capturedResult = it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue