Skip to content

Commit

Permalink
Merge pull request #4 from dturner/develop
Browse files Browse the repository at this point in the history
Update docs, fix some typos and add links
  • Loading branch information
tunjid authored Feb 14, 2024
2 parents 8c19de9 + dd71bcf commit 0e11465
Show file tree
Hide file tree
Showing 7 changed files with 22 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private val headingMarkdown = """
private val introMarkdown = """
By [Adetunji Dahunsi](https://twitter.com/Tunji_D).
State is what is. A declaration of things known at a certain point in time. As time passes however, state changes as data sources backing the state are updated and events happen. In mobile apps this presents a challenge; defining a convenient and concise means to produce state over time.
State is what it is. A declaration of things known at a certain point in time. As time passes however, state changes as data sources backing the state are updated and events happen. In mobile apps this presents a challenge; defining a convenient and concise means to produce state over time.
This page is a [Jetpack Compose](https://developer.android.com/jetpack/compose?gclid=Cj0KCQjwzqSWBhDPARIsAK38LY-nnY_1sTpVvpENJZD5ek-tE18e3MvzE1hXlILdw7uYx1Y47zsvcXkaAlGJEALw_wcB&gclsrc=aw.ds) for [web](https://compose-web.ui.pages.jetbrains.team/) powered interactive experiment that highlights various ways of producing state with a [Flow](https://kotlinlang.org/docs/flow.html). At the end of it, you should have a mental framework to help choose a state production pipeline that is most beneficial to your use cases.
Expand All @@ -79,8 +79,8 @@ Producing state is at its core, is nothing more than consolidating sources of ch
While the tenets of UDF are simple, there's a bit more to implementing it properly especially with `Flows`. Let's start simple. In the following we have a snail along a track. The snail has the following properties:
* It's progress along the track.
* It's color.
* Its progress along the track.
* Its color.
An easy way to represent the state production pipeline for the snail is with a single `MutableStateFlow`:
""".trimIndent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ To produce state then, we simply have to start with an initial state, and increm
Expressing this in code with `Flows` is very concise and requires just two operators:
* `merge`: Used to merge all sources of `Mutation<State>` (Δstate) into a single stream
* `scan`: Used to reduce the stream of `Mutation<State>` into the state being produced.
* [`merge`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/merge.html): Used to merge all sources of `Mutation<State>` (Δstate) into a single stream
* [`scan`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/scan.html): Used to reduce the stream of `Mutation<State>` into the state being produced.
In our snail example, we can express the same state production pipeline with user actions as:
""".trimIndent()
Expand Down Expand Up @@ -113,7 +113,7 @@ class Snail5StateHolder(
speedChanges,
changeEvents,
)
.scan(Snail5State()) { state, mutation -> mutation(state) }
.scan(Snail5State()) { startState, mutation -> mutation(startState) }
.stateIn(...)
fun setSnailColor(index: Int) {
Expand Down Expand Up @@ -150,7 +150,7 @@ It however brings the following advantages:
The switch to `MutableSharedFlow` from `MutableStateFlow` for propagating user actions is because `StateFlow` conflates emissions. If two separate methods attempted to use the same `MutableStateFlow` to emit a `Mutation` of state, the `StateFlow` may only emit the latest `Mutation`. That is `MutableStateFlow` does not guarantee that every update to its `value` property is seen by its collectors.
`MutableSharedFlow` on the other hand has an `emit` method which suspends until the `Mutation` is delivered. This means that multiple coroutines can be launched across several method invocations and call `emit` on the same `MutableShared` `Flow` and none of them will cancel out the other. The order in which they are applied also don't matter as `Mutation` instances just describe changes to state; they are designed to be independent.
`MutableSharedFlow` on the other hand has an `emit` method which suspends until the `Mutation` is delivered. This means that multiple coroutines can be launched across several method invocations and call `emit` on the same `MutableShared` `Flow` and none of them will cancel out the other. The order in which they are applied also doesn't matter as `Mutation` instances just describe changes to state; they are designed to be independent.
""".trimIndent()

Expand Down Expand Up @@ -193,7 +193,7 @@ Drag the snail to place it anywhere on its track. Also, try to hold it in place
private val nineMarkdown = """
That is, we can simply introduce a state change to the `progress` property of the state despite the `progessChanges` flow also contributing to a change of the same property. This is something that would be rather difficult with the `combine` approach. This is because the combine approach only lets you set state properties of state to create new state. The `merge` approach instead lets you `mutate` or change properties of state and apply them to the existing state.
Furthermore both `setSnailColor` and `setProgress` contribute their changes to state using the same `MutableSharedFlow`: `changeEvents`. This approach scales well because no mater how many methods are added that change the `State` from user events, they don't need any more variables to be declared in the state holder class.
Furthermore both `setSnailColor` and `setProgress` contribute their changes to state using the same `MutableSharedFlow`: `changeEvents`. This approach scales well because no matter how many methods are added that change the `State` from user events, they don't need any more variables to be declared in the state holder class.
""".trimIndent()

private val tenMarkdown = """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private val twoCode = """
fun <State: Any> CoroutineScope.stateFlowProducer(
initialState: State,
started: SharingStarted = SharingStarted.WhileSubscribed(),
mutationFlows: List<Flow<Mutation<State>>>
inputs: List<Flow<Mutation<State>>>
) : StateFlowProducer<State>
""".trimIndent()

Expand All @@ -63,7 +63,7 @@ class Snail7StateHolder(
private val stateProducer = scope.stateFlowProducer(
initialState = Snail7State(),
started = SharingStarted.WhileSubscribed(),
mutationFlows = listOf(
inputs = listOf(
speedChanges,
progressChanges,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class Snail11StateHolder(
private val mutator = scope.actionStateFlowProducer<Action, Snail11State>(
initialState = Snail11State(),
started = SharingStarted.WhileSubscribed(),
mutationFlows = listOf(
inputs = listOf(
speedChanges,
progressChanges
),
Expand Down Expand Up @@ -239,7 +239,7 @@ private val nineMarkdown = """
In the above, all there are two sources of state `Mutation`s:
* Data sources in the `mutationFlows` argument; defined as `Flow<Mutation<State>>`
* Data sources in the `inputs` argument; defined as `Flow<Mutation<State>>`
* User events in the `actionTransform` argument; defined as `(Flow<Action>) -> Flow<Mutation<State>>`
Crucially the `actionTransform` takes a `Flow` of all `Action` instances, splits them out into individual `Flow`s for each `Action`, and finally applies `Flow` transformations to each `Action` `Flow` to turn them into `Flow<Mutation<State>>`:
Expand Down
2 changes: 1 addition & 1 deletion docs/demo.js

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions docs/demo.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class SnailStateHolder(
private val stateProducer = scope.stateFlowProducer(
initialState = Snail7State(),
started = SharingStarted.WhileSubscribed(),
mutationFlows = listOf(
inputs = listOf(
speedChanges,
progressChanges,
)
Expand Down

0 comments on commit 0e11465

Please sign in to comment.