English | 中文
https://github.com/airbnb/mavericks
Not RxJava — a minimal Redux built on Kotlin Coroutines Flow.
Read the full introduction (Chinese): Redux-for-Android
A GUI app usually has 3 roles:
- state: describes the data, any immutable data class (must implement the
RxStatemarker interface) - repo: the Redux equivalent of an action — stores, dispatches and updates state
- view: consumes state and triggers repo methods
implementation("io.github.xechoz:mvs:1.1.0")Kotlin Multiplatform library targeting Android and JVM (mvs-android / mvs-jvm are resolved automatically via Gradle module metadata); depends on Kotlin Coroutines. Android minSdk 26.
For development inside this repository, use implementation(project(":lib:mvs")) instead.
// state
data class CounterState(val count: Int = 0) : RxState
// repo (= action)
class CounterRepo : RxRepo<CounterState> by RxRepo(initState = CounterState()) {
fun increment() {
setState {
copy(count = count + 1)
}
}
}
// view
class CounterView {
private val counter = CounterRepo()
private val scope = MainScope()
init {
scope.launch {
counter.onEach(CounterState::count) {
text = "Click $it"
}
}
setOnClickListener {
counter.increment()
}
}
}
// with ViewModel
class FooViewModel : ViewModel(),
RxRepo<FooState> by RxRepo(initState = FooState()) {
fun updateFooToBar(bar: Any) {
setState {
copy(foo = bar)
}
}
}| Member | Description |
|---|---|
val state: State |
Synchronously read the current state |
fun RxRepo<State>.setState(reducer: State.() -> State) |
Update state; the reducer receives the old state as receiver and returns the new state |
fun asFlow(): Flow<State> |
Expose the whole state as a Flow |
suspend fun onEach(action: (State) -> Unit) |
Observe the whole state |
suspend fun <A : Any> onEach(property: KProperty1<State, A>, action: (A) -> Unit) |
Observe a single property |
suspend fun <A : Any> onEach(property: KProperty1<State, A>): Flow<A> |
Expose a property as a Flow, ready to be combined |
fun <State : RxState> RxRepo(initState: State): RxRepo<State> |
Factory function to create a repo |
Notes:
-
setStateis declared as an extension member ofRxRepo, so it can only be called from a repo implementation or its extension functions, preventing external mutation. Add logic to a repo via extension functions:fun TodoRepo.remove(todoRow: TodoRow) { setState { copy(todos = todos.filter { it != todoRow }) } }
-
The subscription lifetime of
onEachis managed by the caller's coroutine scope: call it insidescope.launch { };scope.cancel()stops observing. -
If
onEachis not enough (e.g. combining multiple properties), useasFlow()instead.
- The Todo sample in the
appmodule: Todo.kt, Todo.Repo.kt, Todo.Ui.kt - Blog (Chinese): Redux-for-Android