-
Notifications
You must be signed in to change notification settings - Fork 101
1247: Partial Tree Rerendering #1248
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
Conversation
a393b93
to
0514919
Compare
6f2d156
to
74fb6fb
Compare
0514919
to
7c724a3
Compare
74fb6fb
to
c7d7c15
Compare
06bf903
to
e1f220c
Compare
0a36563
to
717d484
Compare
7d7098f
to
b6e677c
Compare
return flow.value | ||
context.runningWorker(rerenderWorker) { output: T -> | ||
action("rerenderUpdate") { | ||
state = output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prior to this state was not actually changing - so this would not work in the optimized runtime.
8647d68
to
e99c6b6
Compare
717d484
to
646ebf7
Compare
3a5f35a
to
f888f1d
Compare
f888f1d
to
f04cb3d
Compare
2a074ea
to
b40368b
Compare
b40368b
to
a3d67b6
Compare
2773b21
to
00eb7a6
Compare
@@ -211,12 +216,14 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>( | |||
*/ | |||
private fun updateCachedWorkflowInstance( | |||
workflow: StatefulWorkflow<PropsT, StateT, OutputT, RenderingT> | |||
) { | |||
): Boolean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Add a note to the kdoc explaining this new return value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer returning as we just call this relying on its idempotency.
!lastRendering.isInitialized || | ||
subtreeStateDidChange | ||
) { | ||
if (!didUpdateCachedInstance) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be simpler to just call updateCachedWorkflowInstance
unconditionally here? It's idempotent, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point, we check equality there, so we could call it again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just call it again now.
@@ -289,4 +322,17 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>( | |||
SideEffectNode(key, job) | |||
} | |||
} | |||
|
|||
@JvmInline | |||
internal value class Box<T>(private val _value: Any? = Uninitialized) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This seems like a super generic and potentially useful helper, I'd pull it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extracted into workflow-core
as NullableInitBox
with its own tests.
|
||
@Suppress("UNCHECKED_CAST") | ||
fun getOrThrow(): T { | ||
check(isInitialized) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Give this a descriptive error message.
setOf(CONFLATE_STALE_RENDERINGS, RENDER_ONLY_WHEN_STATE_CHANGES) | ||
setOf(CONFLATE_STALE_RENDERINGS, RENDER_ONLY_WHEN_STATE_CHANGES), | ||
setOf(RENDER_ONLY_WHEN_STATE_CHANGES, PARTIAL_TREE_RENDERING), | ||
setOf(CONFLATE_STALE_RENDERINGS, RENDER_ONLY_WHEN_STATE_CHANGES, PARTIAL_TREE_RENDERING), | ||
).asSequence() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not in scope but… why isn't this just sequenceOf(…)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm not sure tbh. the way it was written originally is likely just a mistake?
// If we are using the optimization, always return to the parent, so we carry a path that | ||
// notes that the subtree did change all the way to the root. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this comment, but I'm missing the middle reason why the optimization requires propagating change status all the way up but we don't need that without the optimization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need that without the optimization because there is nothing to output from the root of the runtime.
The output has propagated as far as it needs to causing all corresponding state changes.
However, the root and the path down to the changed nodes must always re-render now, so this is the implementation detail of how we get subtreeStateDidChange = true
on that entire path to the root.
c1623df
to
8f303c5
Compare
workflow-runtime/src/commonMain/kotlin/com/squareup/workflow1/RuntimeConfig.kt
Outdated
Show resolved
Hide resolved
workflow-runtime/src/commonMain/kotlin/com/squareup/workflow1/RuntimeConfig.kt
Outdated
Show resolved
Hide resolved
@@ -260,6 +285,7 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>( | |||
* Applies [action] to this workflow's [state] and then passes the resulting [ActionApplied] | |||
* via [emitAppliedActionToParent] to the parent, with additional information as to whether or | |||
* not this action has changed the current node's state. | |||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
snip
workflow-runtime/src/commonMain/kotlin/com/squareup/workflow1/internal/WorkflowNode.kt
Show resolved
Hide resolved
* Check [isInitialized] to see if the value has been assigned. | ||
*/ | ||
@JvmInline | ||
public value class NullableInitBox<T>(private val _value: Any? = Uninitialized) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't take in at first that this is a value class
. Cool!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was a @zach-klippenstein special when we were noodling on this.
If PARTIAL_TREE_RENDERING is in the RuntimeConfig then, Track whether or not the state (or state of a descendant) changed in the WorkflowNode. Pass lastRendering if its not. Also adds tests for this behavior and expands the test runtime matrix. Also adds shards for the new runtime for instrumentation tests.
…RuntimeConfig.kt Co-authored-by: Ray Ryan <[email protected]>
…RuntimeConfig.kt Co-authored-by: Ray Ryan <[email protected]>
…internal/WorkflowNode.kt Co-authored-by: Ray Ryan <[email protected]>
6384fa7
to
7d3a6fe
Compare
Re-render the node only if:
In order to do this, we have to propagate the action cascade all the way up to the root to record a single path of changed nodes.
Note we currently do not re-render if the workflow instance (the behavior definition instance, not the node) changed (unless the state also changed!), assuming the new workflow is still compatible with the old rendering because its identifier did not change.
Closes #1247.