Comments (9)
Note that StateFlowImpl
appears to use default (instance) equality.
from workflow-kotlin.
Thinking about this more...
we have had 2 main use cases for a StateFlow :
- So that we could get
.value
out of theStateFlow
ininitialState
to avoid one extra bootstrap re-rendering (for performance reasons). If we are going to do thatstateIn
ininitialState
then it either won't be synchronous (the suspending version) or we already have the correctinitialValue
synchronously (as props or otherwise), in which case we don't really need aStateFlow
frominitialState
onwards. We can just populate the Workflow's state with the initial value and use theflow
form aWorker
inrender()
. - The 2nd case is if we have operators that we want to do on one or more upstream StateFlows. Given any operator has work to do and brings this back to a
flow
, we need tostateIn
after the operator in order to get it back to aStateFlow
to access.value
synchronously ininitialState
. The operator combination is likely a concern of the Workflow usage only, so we want to be able to model the code for it there. It would be better to be able to do the operators and then astateIn
attached to the Workflow's lifecycle itself (thus going back to providing theCoroutineScope
ininitialState
). However, as I think through this now I'm not sure this case doesn't just collapse into the first. If we have all upstreamStateFlow
signals we can.value
them all already ininitialState
and then run the operator/combination logic synchronously (it better be fast) ininitialState
to get a value to populate our state without extra re-renders. Then we're happy enough if the operators result in aflow
to collect from in aWorker
.
So now I've convinced myself that I don't see a case where we need CoroutineScope
in initialState
to accomplish something we couldn't before. It might be a useful convenience, but I need to think about whether or not we want to enable that convenience vs. encouraging doing the above steps. 🤔
from workflow-kotlin.
Case one is less interesting now that #992 has landed, but this still seems like it would solve a lot of other problems.
from workflow-kotlin.
still seems like it would solve a lot of other problems.
In terms of discoverability and having good tools 'at hand' you mean?
I feel like I've ruled out what I thought before were the main use cases in terms of being blocked without this.
from workflow-kotlin.
You have a much clearer picture of the situation than I do. Let's definitely not build this until we have real people with real use cases in hand.
from workflow-kotlin.
I have a case where I need a CoroutineScope that lasts for the duration of a workflow. I need a scope and object from a callback in order to create a new state. With the current implementation it's going to look like something like this on the render function
context.runningSideEffect("scope") {
val scope = this
context.actionSink.send(action {
val newState = state.result?.let { NewState(NewObject(scope, it))}
val oldState = state.copy(scope = scope)
state = newState ?: oldState
})
// suspend forever
}
return Rendering(
onObjectCreated = context.eventHandler {
val newState = state.scope?.let { NewState(NewObject(it, result))}
val oldState = state.copy(result = result)
state = newState ?: oldState
}
)
This creates a funky race condition where I have to keep track of two callbacks and having a CoroutineScope
available for the workflow would be very helpful.
from workflow-kotlin.
From an offline conversation with @steve-the-edwards:
public abstract class SessionWorkflow<
SessionT,
in PropsT,
StateT,
out OutputT,
out RenderingT
> : Workflow<PropsT, OutputT, RenderingT>, IdCacheable {
public abstract fun onSessionStarted(scope: CoroutineScope): SessionT
public abstract fun initialState(
session: SessionT,
props: PropsT,
snapshot: Snapshot?
): StateT
public abstract fun render(
session: SessionT,
renderProps: PropsT,
renderState: StateT,
context: RenderContext
): RenderingT
public abstract class StatefulWorkflow<
in PropsT,
StateT,
out OutputT,
out RenderingT
> : SessionWorkflow<Unit, PropsT, OutputT, RenderingT>, IdCacheable
public final fun initialState(
session: SessionT,
props: PropsT,
snapshot: Snapshot?
): StateT = initialState(props, snapshot)
public abstract final initialState(
props: PropsT,
snapshot: Snapshot
): StateT
One big idea here is that SessionT
could be a Dagger Component, built from Dagger modules that could include providers for a StateFlow
whose lifetime needs to match that of a particular WorkflowNode
. Any child workflow instantiated from that Component would be able get at that StateFlow
, or, say, a repository that wraps it, via constructor injection. Or those children could constructor inject child workflows that in turn can inject that StateFlow
…
I like the idea of SessionWorkflow
as a separate type because I think that 99% of Workflow implementations should not be thinking about SessionT
directly. It's useful for high level lifecycle needs -- the LoggedInWorkflow, the SettingsAppletWorkflow. It's just noise for the average FooScreen leaf workflow.
from workflow-kotlin.
So now the next question is: do we really need SessionT
for this pattern? Or could LoggedInWorkflow
simply manage its Dagger component as part of its StateT
? I bet it could, and in fact we may have some apps doing exactly that already.
So did I just talk myself back into the original plan of simply adding a CoroutineScope
param to initialState()
, and advising folks to take advantage of this by adding a Dagger Component to StateT
? Instance equality would prevent them from blowing up any optimizations.
from workflow-kotlin.
So did I just talk myself back into the original plan of simply adding a CoroutineScope param to initialState()
Perhaps, because without a notion of SessionT
or WorkflowLocal
we wouldn't be able to pass any computed StateFlow
from onSessionStarted
through to the StateT
so it would need to be in initialState
from workflow-kotlin.
Related Issues (20)
- `ComposeView` and `IllegalStateException: You can consumeRestoredStateForKey only after super.onCreate of corresponding component` HOT 9
- Rework `ScreenViewFactoryFinder` s.t. it prefers `ViewRegistry` entries for the built in types too
- Consider open sourcing our wrappers for onbackpresseddispatcher HOT 2
- Remove mobile.dev integration
- Remove references to 'tick' from runtime in the tests.
- Consider providing access to the CoroutineScope in `initialState` HOT 1
- Side effects / Workers / LifecycleWorker methods not called if rendered/not rendered without ever yielding dispatch HOT 8
- `ComponentDialog.asDialogHolderWithContent` should provide decor view as container
- UI Test shards are flaky, perhaps interfering with each other? HOT 2
- Introduce `BackStackScreen.flatten()`
- Un-deprecate runningWorker for LifecycleWorker HOT 2
- SessionWorkflow throws when trying to unit test it with RenderTester#testRender
- Deprecate one/disambiguate between launchForTestingFromStart/StateWith and headlessIntegrationTest HOT 1
- Consider removing `ViewEnvironment` parameter from `RenderWorkflowIn`
- Offer config option to skip rendering when new state == old HOT 3
- Showing Overlay causes crash when using AnimatedContent to animate screens HOT 2
- RenderContext passed into StatefulWorkflow#render is new instance every time HOT 3
- Deprecate `runningWorker` and add `@ReplaceWith` for `runningSideEffect` alternative
- Remove JMH Benchmarks
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from workflow-kotlin.