Skip to content

Commit d9ace4f

Browse files
988: Cache RenderContext per instance
1 parent 1bf6f2f commit d9ace4f

File tree

2 files changed

+46
-14
lines changed

2 files changed

+46
-14
lines changed

workflow-core/src/commonMain/kotlin/com/squareup/workflow1/StatelessWorkflow.kt

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
package com.squareup.workflow1
55

6-
import kotlin.LazyThreadSafetyMode.NONE
76
import kotlin.jvm.JvmMultifileClass
87
import kotlin.jvm.JvmName
98

@@ -33,12 +32,6 @@ public abstract class StatelessWorkflow<in PropsT, out OutputT, out RenderingT>
3332
) : BaseRenderContext<@UnsafeVariance PropsT, Nothing, @UnsafeVariance OutputT> by
3433
baseContext as BaseRenderContext<PropsT, Nothing, OutputT>
3534

36-
@Suppress("UNCHECKED_CAST")
37-
private val statefulWorkflow = Workflow.stateful<PropsT, Unit, OutputT, RenderingT>(
38-
initialState = { Unit },
39-
render = { props, _ -> render(props, RenderContext(this, this@StatelessWorkflow)) }
40-
)
41-
4235
/**
4336
* Called at least once any time one of the following things happens:
4437
* - This workflow's [renderProps] change (via the parent passing a different one in).
@@ -70,11 +63,39 @@ public abstract class StatelessWorkflow<in PropsT, out OutputT, out RenderingT>
7063
* Satisfies the [Workflow] interface by wrapping `this` in a [StatefulWorkflow] with `Unit`
7164
* state.
7265
*
73-
* This method is called a few times per instance, but we don't need to allocate a new
74-
* [StatefulWorkflow] every time, so we store it in a private property.
66+
* This is only called when the instance of the Workflow is created, so we can recreate this each
67+
* time.
7568
*/
76-
final override fun asStatefulWorkflow(): StatefulWorkflow<PropsT, *, OutputT, RenderingT> =
77-
statefulWorkflow
69+
final override fun asStatefulWorkflow(): StatefulWorkflow<PropsT, *, OutputT, RenderingT> {
70+
return object : StatefulWorkflow<PropsT, Unit, OutputT, RenderingT>() {
71+
// We want to cache the render context so that we don't have to recreate it each time
72+
// render() is called.
73+
private var cachedStatelessRenderContext:
74+
StatelessWorkflow<PropsT, OutputT, RenderingT>.RenderContext? = null
75+
private var cachedStatefulRenderContext:
76+
StatefulWorkflow<PropsT, Unit, OutputT, RenderingT>.RenderContext? = null
77+
78+
override fun initialState(
79+
props: PropsT,
80+
snapshot: Snapshot?
81+
) = Unit
82+
83+
override fun render(
84+
renderProps: PropsT,
85+
renderState: Unit,
86+
context: RenderContext
87+
): RenderingT {
88+
if (cachedStatelessRenderContext == null || context != cachedStatefulRenderContext) {
89+
// Recreate it if the underlying context changed.
90+
cachedStatelessRenderContext = RenderContext(context, this@StatelessWorkflow)
91+
}
92+
cachedStatefulRenderContext = context
93+
return render(renderProps, cachedStatelessRenderContext!!)
94+
}
95+
96+
override fun snapshotState(state: Unit): Snapshot? = null
97+
}
98+
}
7899
}
79100

80101
/**

workflow-runtime/src/commonMain/kotlin/com/squareup/workflow1/WorkflowInterceptor.kt

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ public object NoopWorkflowInterceptor : WorkflowInterceptor
268268
/**
269269
* Returns a [StatefulWorkflow] that will intercept all calls to [workflow] via this
270270
* [WorkflowInterceptor].
271+
*
272+
* This is called once for each instance/session of a Workflow being intercepted. So we cache the
273+
* render context for re-use within that [WorkflowSession].
271274
*/
272275
@OptIn(WorkflowExperimentalApi::class)
273276
internal fun <P, S, O, R> WorkflowInterceptor.intercept(
@@ -277,6 +280,10 @@ internal fun <P, S, O, R> WorkflowInterceptor.intercept(
277280
workflow
278281
} else {
279282
object : SessionWorkflow<P, S, O, R>() {
283+
284+
private var cachedOriginalRenderContext: StatefulWorkflow<P, S, O, R>.RenderContext? = null
285+
private var cachedInterceptedRenderContext: StatefulWorkflow<P, S, O, R>.RenderContext? = null
286+
280287
override fun initialState(
281288
props: P,
282289
snapshot: Snapshot?,
@@ -298,9 +305,13 @@ internal fun <P, S, O, R> WorkflowInterceptor.intercept(
298305
renderState,
299306
context,
300307
proceed = { props, state, interceptor ->
301-
val interceptedContext = interceptor?.let { InterceptedRenderContext(context, it) }
302-
?: context
303-
workflow.render(props, state, RenderContext(interceptedContext, this))
308+
if (cachedInterceptedRenderContext == null || cachedInterceptedRenderContext != context) {
309+
val interceptedRenderContext = interceptor?.let { InterceptedRenderContext(context, it) }
310+
?: context
311+
cachedInterceptedRenderContext = RenderContext(interceptedRenderContext, this)
312+
}
313+
cachedOriginalRenderContext = context
314+
workflow.render(props, state, cachedInterceptedRenderContext!!)
304315
},
305316
session = workflowSession,
306317
)

0 commit comments

Comments
 (0)