Skip to content

*BREAKING* Introduces BaseRenderContext.remember and stable eventHandlers. #1267

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

Merged
merged 1 commit into from
Mar 10, 2025

Conversation

rjrjr
Copy link
Contributor

@rjrjr rjrjr commented Feb 21, 2025

Workflow makes it very convenient to render view models with anonymous lambdas as their event handler functions. Compose hates that.

To address that mismatch without forcing everyone to retrofit their apps to use event objects instead of lambdas (it's a little late for that!) we introduce support for stable event handlers: anonymous lambdas whose identity looks the same to Compose across updates.

In order to do this we're breaking the existing eventHandler and safeEventHandler functions a bit.

  • We introduce a new optional remember: Boolean? = null parameter. Set that true to get the new stability. If you leave it to the default null we look for a new STABLE_EVENT_HANDLER : RuntimeConfigOption to decide what to do. If you set that config option on an existing app and make no other changes, all of your existing eventHandler functions will be stable.

  • When remember is true, the existing name parameter becomes weight bearing. It's no longer just a logging aid, it's part of a key identifying your stable lambda. The other parts of the key are its return type, and the types of any of its parameters. Duplicating a key within a particular render() call is a runtime error, similar to the rules for renderChild, runningWorker, and runningSideEffect.

  • To make it easier to find fix and prevent those new runtime errors, testRender and WorkflowTestParams now accept optional RuntimeConfig parameters, and throw appropriate errors if STABLE_EVENT_HANDLER is set. testRender also now honors JvmTestRuntimeConfigTools.getTestRuntimeConfig().

  • Most of the eventHandler functions have also been changed to inline -- necessary so that we can reify their parameter types for the key scheme described above

All of this is built on a new BaseRenderContext.remember primitive, which provides a light weight mechanism to save a bit of state across a workflow session without having to find room for it in StateT.

BaseRenderContext also now provides val runtimeConfig: RuntimeConfig in support of all of the above.

@rjrjr rjrjr force-pushed the ray/remember-event-handler branch 6 times, most recently from d4cdaa3 to a137249 Compare February 25, 2025 00:34
@rjrjr rjrjr force-pushed the ray/remember-event-handler branch 6 times, most recently from 95498e2 to fec7178 Compare February 26, 2025 01:40
@rjrjr rjrjr force-pushed the ray/remember-event-handler branch from fc02943 to 1f63015 Compare March 1, 2025 04:42
@rjrjr rjrjr changed the title remember event handler wip: Introduces remember and builds stable eventHandlers on top of it. Mar 1, 2025
@rjrjr rjrjr force-pushed the ray/remember-event-handler branch 2 times, most recently from 77672ac to 7348024 Compare March 3, 2025 20:52
@rjrjr rjrjr force-pushed the ray/remember-event-handler branch 2 times, most recently from 5528d91 to bc7a48d Compare March 7, 2025 23:01
@rjrjr rjrjr changed the title wip: Introduces remember and builds stable eventHandlers on top of it. *BREAKING* Introduces BaseRenderContext.remember and stable eventHandlers. Mar 7, 2025
@rjrjr rjrjr force-pushed the ray/remember-event-handler branch 5 times, most recently from 6452f7f to 0b76577 Compare March 7, 2025 23:32
@rjrjr rjrjr marked this pull request as ready for review March 7, 2025 23:32
@rjrjr rjrjr requested a review from a team as a code owner March 7, 2025 23:32
@rjrjr rjrjr requested review from a team and zach-klippenstein as code owners March 7, 2025 23:32
/**
* Convenience alias for working with [WorkflowAction.Updater].
*/
public typealias Updater<P, S, O> = WorkflowAction<P, S, O>.Updater
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

@@ -136,7 +327,11 @@ public fun <PropsT, OutputT, RenderingT> RenderContext(
* their own internal state.
*/
public inline fun <PropsT, OutputT, RenderingT> Workflow.Companion.stateless(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this change, but did we end up discouraging the use of these functions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. They're not super widely used but they're very handy when they're handy.

Comment on lines +112 to +114
private fun checkNotFrozen(reason: () -> String = { "" }) = check(!frozen) {
"RenderContext cannot be used after render method returns" +
"${reason().takeUnless { it.isBlank() }?.let { " ($it)" }}"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

…andlers`.

Workflow makes it very convenient to render view models with anonymous lambdas as their event handler functions. Compose hates that.

To address that mismatch without forcing everyone to retrofit their apps to use event objects instead of lambdas (it's a little late for that!) we introduce support for stable event handlers: anonymous lambdas whose identity looks the same to Compose across updates.

In order to do this we're breaking the existing `eventHandler` and `safeEventHandler` functions a bit.

- We introduce a new optional `remember: Boolean? = null` parameter. Set that true to get the new stability. If you leave it to the default `null` we look for a new `STABLE_EVENT_HANDLER : RuntimeConfigOption` to decide what to do. If you set that config option on an existing app and make no other changes, all of your existing `eventHandler` functions will be stable.

- When `remember` is true, the existing `name` parameter becomes weight bearing. It's no longer just a logging aid, it's part of a key identifying your stable lambda. The other parts of the key are its return type, and the types of any of its parameters. Duplicating a key within a particular `render()` call is a runtime error, similar to the rules for `renderChild`, `runningWorker`, and `runningSideEffect`.

- To make it easier to find fix and prevent those new runtime errors `testRender` and `WorkflowTestParams` now accept optional `RuntimeConfig` parameters, and throw appropriate errors if `STABLE_EVENT_HANDLER` is set. `testRender` also now honors `JvmTestRuntimeConfigTools.getTestRuntimeConfig()`.

- Most of the `eventHandler` functions have also been changed to `inline` -- necessary so that we can reify their parameter types for the key scheme described above

All of this is built on a new `BaseRenderContext.remember` primitive, which provides a light weight mechanism to save a bit of state across a workflow session without having to find room for it in `StateT`.

`BaseRenderContext` also now provides `val runtimeConfig: RuntimeConfig` in support of all of the above.
@rjrjr rjrjr force-pushed the ray/remember-event-handler branch from 0b76577 to 60f7ea5 Compare March 10, 2025 21:55
@@ -79,6 +80,7 @@ private fun AnimatedCounter(
transitionSpec = {
((slideInVertically() + fadeIn()).togetherWith(slideOutVertically() + fadeOut()))
.using(SizeTransform(clip = false))
}
},
label = ""
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this fixed a compiler warning? Will check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, seems to serve no purpose, will revert.

@@ -136,7 +327,11 @@ public fun <PropsT, OutputT, RenderingT> RenderContext(
* their own internal state.
*/
public inline fun <PropsT, OutputT, RenderingT> Workflow.Companion.stateless(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. They're not super widely used but they're very handy when they're handy.

@rjrjr rjrjr merged commit 4d8f5bc into main Mar 10, 2025
39 checks passed
@rjrjr rjrjr deleted the ray/remember-event-handler branch March 10, 2025 22:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants