Skip to content

Commit

Permalink
988: Cache RenderContext per instance
Browse files Browse the repository at this point in the history
  • Loading branch information
steve-the-edwards committed Jan 21, 2025
1 parent 1bf6f2f commit d54c92e
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ public abstract class StatelessWorkflow<in PropsT, out OutputT, out RenderingT>
) : BaseRenderContext<@UnsafeVariance PropsT, Nothing, @UnsafeVariance OutputT> by
baseContext as BaseRenderContext<PropsT, Nothing, OutputT>

@Suppress("UNCHECKED_CAST")
private val statefulWorkflow = Workflow.stateful<PropsT, Unit, OutputT, RenderingT>(
initialState = { Unit },
render = { props, _ -> render(props, RenderContext(this, this@StatelessWorkflow)) }
Expand Down Expand Up @@ -70,11 +69,35 @@ public abstract class StatelessWorkflow<in PropsT, out OutputT, out RenderingT>
* Satisfies the [Workflow] interface by wrapping `this` in a [StatefulWorkflow] with `Unit`
* state.
*
* This method is called a few times per instance, but we don't need to allocate a new
* [StatefulWorkflow] every time, so we store it in a private property.
* This is only called when the instance of the Workflow is created, so we can recreate this each
* time.
*/
final override fun asStatefulWorkflow(): StatefulWorkflow<PropsT, *, OutputT, RenderingT> =
statefulWorkflow
final override fun asStatefulWorkflow(): StatefulWorkflow<PropsT, *, OutputT, RenderingT> {
return object : StatefulWorkflow<PropsT, Unit, OutputT, RenderingT>() {
// We want to cache the render context so that we don't have to recreate it each time
// render() is called.
private var cachedRenderContext:
StatelessWorkflow<PropsT, OutputT, RenderingT>.RenderContext? = null

override fun initialState(
props: PropsT,
snapshot: Snapshot?
) = Unit

override fun render(
renderProps: PropsT,
renderState: Unit,
context: RenderContext
): RenderingT {
if (cachedRenderContext == null) {
cachedRenderContext = RenderContext(context, this@StatelessWorkflow)
}
return render(renderProps, cachedRenderContext!!)
}

override fun snapshotState(state: Unit): Snapshot? = null
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ public object NoopWorkflowInterceptor : WorkflowInterceptor
/**
* Returns a [StatefulWorkflow] that will intercept all calls to [workflow] via this
* [WorkflowInterceptor].
*
* This is called once for each instance/session of a Workflow being intercepted. So we cache the
* render context for re-use within that [WorkflowSession].
*/
@OptIn(WorkflowExperimentalApi::class)
internal fun <P, S, O, R> WorkflowInterceptor.intercept(
Expand All @@ -277,6 +280,9 @@ internal fun <P, S, O, R> WorkflowInterceptor.intercept(
workflow
} else {
object : SessionWorkflow<P, S, O, R>() {

private var cachedRenderContext: StatefulWorkflow<P, S, O, R>.RenderContext? = null

override fun initialState(
props: P,
snapshot: Snapshot?,
Expand All @@ -298,9 +304,12 @@ internal fun <P, S, O, R> WorkflowInterceptor.intercept(
renderState,
context,
proceed = { props, state, interceptor ->
val interceptedContext = interceptor?.let { InterceptedRenderContext(context, it) }
?: context
workflow.render(props, state, RenderContext(interceptedContext, this))
if (cachedRenderContext == null) {
val interceptedRenderContext = interceptor?.let { InterceptedRenderContext(context, it) }
?: context
cachedRenderContext = RenderContext(interceptedRenderContext, this)
}
workflow.render(props, state, cachedRenderContext!!)
},
session = workflowSession,
)
Expand Down

0 comments on commit d54c92e

Please sign in to comment.