Skip to content

Commit

Permalink
Updating readme.
Browse files Browse the repository at this point in the history
  • Loading branch information
Laimiux committed Jun 28, 2022
1 parent d30fd57 commit 2fff920
Showing 1 changed file with 154 additions and 0 deletions.
154 changes: 154 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,160 @@ deterministic, composable, testable applications.

[Check samples](samples)

## Quick Example
As a quick example, we'll show how a counter application looks which shows
current amount and allows you to increment or decrement it.
```kotlin
class CounterFormula : Formula<Unit, State, Output>() {

data class State(
val currentCount: Int
)

data class Output(
val countText: String,
val onIncrement: () -> Unit,
val onDecrement: () -> Unit,
)

override fun initialState(input: Unit): State {
return State(currentCount = 0)
}

override fun Snapshot<Unit, State>.evaluate(): Evaluation<Output> {
val currentCount = state.currentCount
val output = Output(
countText = "Count: $currentCount",
onIncrement = context.callback {
val newState = state.copy(
currentCount = currentCount + 1
)
transition(newState)
},
onDecrement = context.callback {
val newState = state.copy(
currentCount = currentCount - 1
)
transition(newState)
}
)
return Evaluation(output = output)
}
}

// Vanilla Android rendering logic
val currentCountTextView: TextView = ...
val incrementButton: Button = ...
val decrementButton: Button = ...

// Converting to Coroutine Flow.
val counterOutput: Flow<CounterFormula.Output> = CounterFormula().toFlow()
scope.launch {
counterOutput.collect { output ->
counterTextView.text = output.countText
incrementButton.setOnClickListener { output.onIncrement() }
decrementButton.setOnClickListener { output.onDecrement() }
}
}

// Alternatively, converting to RxJava observable.
val counterOutput: Observable<CounterFormula.Output> = CounterFormula().toObservable()
counterOutput.subscribe { output ->
counterTextView.text = output.countText
incrementButton.setOnClickListener { output.onIncrement() }
decrementButton.setOnClickListener { output.onDecrement() }
}
```
## Quick Example - Testing
```kotlin
@Test fun `counter will not allow to decrement below 0`() {
CounterFormula()
.test()
.output { Truth.assertThat(count).isEqualTo("Count: 0") }
.output { onDecrement() }
// This fails right now as we don't have this logic added.
.output { Truth.assertThat(count).isEqualTo("Count: 0") }
}
```

## Quick Example - Async code
To display how to handle asynchronous actions, let's update previous counter example where
we persist and read current counter state from disk.
```kotlin
// We use simple in-memory implementation which could be changed to support real disk cache.
class CounterRepository {
private var count: Int = 0

fun saveCurrentCount(count: Int) {
this.count = count
}

fun getCounterState(): Observable<Int> {
return Observable.fromCallable { count }
}
}

class CounterFormula(val repository: CounterRepository) : Formula<Unit, State, Output>() {

// We make currentCount nullable to indicate not-loaded / loaded states.
data class State(
val currentCount: Int?
)

data class Output(
val countText: String,
val onIncrement: () -> Unit,
val onDecrement: () -> Unit,
)

override fun initialState(input: Unit): State {
return State(currentCount = null)
}

override fun Snapshot<Unit, State>.evaluate(): Evaluation<Output> {
val currentCount = state.currentCount
val output = if (currentCount == null) {
Output(
countText = "Loading",
onIncrement = {},
onDecrement = {}
)
} else {
Output(
countText = "Count: $currentCount",
onIncrement = context.callback {
val newState = state.copy(
currentCount = currentCount + 1
)
transition(newState) {
repository.saveCurrentCount(newState.currentCount)
}
},
onDecrement = context.callback {
val newState = state.copy(
currentCount = currentCount - 1
)
transition(newState) {
repository.saveCurrentCount(newState.currentCount)
}
}
)
}

return Evaluation(
output = output,
actions = context.actions {
// We add an action which gets the counter state and updates our state.
RxAction.fromObservable { repository.getCounterState() }.onEvent { currentCount ->
val newState = state.copy(currentCount = currentCount)
transition(newState)
}
}
)
}
}
```

## Android Module
The Android module provides declarative API to connect reactive state management to Android Fragments.
This module has been designed for gradual adoption. You can use as much or as little of it as you like.
Expand Down

0 comments on commit 2fff920

Please sign in to comment.