-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding simple examples to README.md #268
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a way to use Flow here as well yet? I don't think we support it yet, but if we do, maybe we could add that example too. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, though I'm not yet sure how to best incorporate in in examples as there is RxJava as well. I will think about it more |
||
} | ||
) | ||
} | ||
} | ||
``` | ||
|
||
## 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. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we can show the example without using RxJava. Possible? Or maybe this should switch to
Flow
, might be more familiar to Android devs now.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
Flow
example as well