Bloc DSL
There's a DSL to make the definition of Blocs
easy. We have encountered some of that DSL already in the chapters about Reducers, Thunks and Initializers. Here's a (dummy) example to give an overview of all of possible functions :
data class CountState(val count: Int)
sealed class CountAction
object Increment : CountAction()
object Decrement : CountAction()
sealed class SideEffect
object LogEvent : SideEffect()
fun bloc(context: BlocContext) = bloc<CountState, CountAction, SideEffect, CountState>(
context,
blocState(CountState(1))
) {
// Initializer
onCreate {
logger.d("Bloc is starting")
}
// Thunk
thunk {
logger.d("Run an asynchronous operation")
dispatch(action)
}
// Reducer with side effect
reduceAnd<Increment> {
state.copy(count = state.count + 1) and LogEvent
}
// Reducer without side effect
reduceAnd<Decrement> {
state.copy(count = state.count + 1).noSideEffect()
}
// Reducers without side effects
reduce<Increment> { state.copy(count = state.count + 1) }
reduce<Decrement> { state.copy(count = state.count - 1) }
// side effect
sideEffect<Decrement> { LogEvent }
// catch-all reducer with side effect
reduce {
when (action) {
is Increment -> state.copy(count = state.count + 1)
else -> state.copy(count = state.count - 1)
}
}
// catch-all reducer without side effect
reduceAnd {
when (action) {
is Increment -> state.copy(count = state.count + 1).noSideEffect()
else -> state.copy(count = state.count - 1) and LogEvent
}
}
}
BlocBuilder
While this is great to define the Bloc
functions, there are also helper functions that make the process of declaring Blocs
even simpler/shorter. In above example the Bloc
was declared using the full syntax:
bloc<CountState, CountAction, SideEffect, CountState>(
context,
blocState = blocState(CountState(1))
) {
If we only need a standard BlocState
(see also BlocStateBuilder) we can replace the blocState
parameter by an initialValue
:
bloc<CountState, CountAction, SideEffect, CountState>(
context,
initialValue = CountState(1)
) {
The framework will create a BlocState
with that initial value automatically.
In many cases the State
and Proposal
are identical (like in the example above) so we can get rid of the generic type for the Proposal
:
// with blocState
bloc<CountState, CountAction, SideEffect>(
context,
blocState = blocState(CountState(1))
) {
// with initialValue
bloc<CountState, CountAction, SideEffect>(
context,
initialValue = CountState(1)
) {
Using this syntax the type of Proposal
will be inferred as State
.
If SideEffects
aren't needed (more often than not we won't need them), we can simplify the syntax even more:
// with blocState
bloc<CountState, CountAction>(
context,
blocState = blocState(CountState(1))
) {
// with initialValue
bloc<CountState, CountAction>(
context,
initialValue = CountState(1)
) {
Using this syntax the type of SideEffect
will be set to Unit
(we can't use Nothing
because side effects are of type Any
and Nothing
is not a sub type of Any
).