Added
ReduceAction — typed, self-reducing actions that fix the lost-update problem.
When multiple SideEffects read a state snapshot, compute a new collection from it,
and dispatch the whole collection as a replacement, the last writer silently
overwrites the others' changes. awaitState() cannot prevent this — it guarantees
a fresh read, not a safe write.
A ReduceAction is a normal, named action that carries only the delta in its
properties and its own reduce(state) merge. It is dispatched like any other
action; the state machine applies reduce against the current state — not the
snapshot the SideEffect saw — so concurrent updates compose instead of overwriting
each other:
data class MergeItemsAction(val entries: Map<String, Int>) : ReduceAction<ItemsState> {
override fun reduce(state: ItemsState) = state.copy(items = state.items + entries)
}
// In a SideEffect: heavy computation stays here, off the state machine
val delta = heavyProcessing(response)
// Dispatch the operation like any other action; reduce() merges into the CURRENT state
dispatch(MergeItemsAction(delta))Because it is a regular Action, it stays part of the flow: middleware sees it,
it appears in actions/actionStates with its payload visible in logs, and other
SideEffects can react to it. Keep reduce() fast and pure — carry precomputed
deltas, and combine them with the current state. Works uniformly for maps, lists,
sets, and scalar fields.
See the Concurrency and Collections
wiki page for the full guidance, including list-specific rules (operate by stable
IDs or anchors computed inside reduce(), never by snapshot-derived indexes).
Removed
setState and SetStateAction (introduced in 1.2.0) are removed.
setState solved the same lost-update problem but exposed an anonymous write
path that let code bypass typed actions entirely — against Flywheel's design,
where every state change is a named action in the flow. ReduceAction provides
the same atomicity as part of the action vocabulary.
Migrating from 1.2.0 (if you adopted setState):
// 1.2.0
setState("mergeItems") { copy(items = items + delta) }
// 1.3.0
data class MergeItemsAction(val delta: Map<String, Int>) : ReduceAction<ItemsState> {
override fun reduce(state: ItemsState) = state.copy(items = state.items + delta)
}
dispatch(MergeItemsAction(delta))Code coming from 1.1.7 or earlier needs no changes.
Install
Gradle:
implementation("com.msabhi:flywheel:1.3.0") // Kotlin Multiplatform
implementation("com.msabhi:flywheel-android:1.3.0") // AndroidSwift Package Manager — add https://github.com/abhimuktheeswarar/Flywheel.git
and select 1.3.0.
Full changelog: v1.2.0...v1.3.0