Skip to content
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

Update docs, fix some typos and add links #4

Merged
merged 2 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading