typealias Handler<State, Action, Mutation> = (State, Action) -> Flow<Mutation>
(source)
Handler is a function that receives the current state and an action that just happened and acts on it.
It returns a kotlinx.coroutines.flow.Flow of Mutation which is the way it can mutate the current state if it needs to.
Note: implementations should return straight away and do any operations inside the flow.
Keep also in mind that Mutations should indicate how to change the state, but should not rely on/assume what the current state is (as of when the action was emitted).
// Good example: it returns a Flow immediately
val good: Handler<State, Action, Mutation> = { _, _ ->
flow {
slowOperation1()
emit(Mutation.A)
slowOperation2()
emit(Mutation.B)
}
}
// Bad example: doing something slow before returning flow
val bad: Handler<State, Action, Mutation> = { _, _ ->
slowOperation1()
flow {
emit(Mutation.A)
slowOperation2()
emit(Mutation.B)
}
}
// How to create good mutations:
data class State(val counter: Int)
sealed class Mutation {
data class Add(val amount: Int)
// The next mutation is a bad example, don't use it
data class SetValue(val newValue: Int)
}
// Good example - explain how to modify the state
val good: Handler<State, Action, Mutation> = { _, _ ->
// simulate long work
delay(1_000)
emit(Mutation.Add(1))
}
// [good] always works. Even if 3 actions are emitted quickly, each one will emit an adition of 1 and the
// reducer will work properly
// Bad example - don't assume that state is still up to date. That is the reducer's job.
val bad: Handler<State, Action, Mutation> = { state, _ ->
// simulate long work
delay(1_000)
emit(Mutation.SetValue(state.counter+1))
}
// [bad] has a bug. If several actions are emitted in less than a second (the time it takes to do the long
// operation in this example), when each of these actions complete, it will emit a mutation to set the
// value to initial state + 1, overriding the changes of the previous actions that meanwhile had changed the
// current state.