Skip to content

Avac web core generic signals#1061

Draft
ditman wants to merge 8 commits intogoogle:mainfrom
ditman:avac-web-core-generic-signals
Draft

Avac web core generic signals#1061
ditman wants to merge 8 commits intogoogle:mainfrom
ditman:avac-web-core-generic-signals

Conversation

@ditman
Copy link
Copy Markdown
Collaborator

@ditman ditman commented Apr 2, 2026

Description

Attempting to fix React CI for #1009; do not merge this.

Pre-launch Checklist

If you need help, consider asking for advice on the discussion board.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the A2UI core to be framework-agnostic by abstracting reactivity through a new FrameworkSignal interface and utilizing generics across the state and rendering layers. The React renderer has been updated to provide a Preact-based signal implementation, and the explorer app and tests have been adjusted accordingly. Feedback highlights several critical areas for improvement: the FrameworkSignal interface should be extended to support batching to prevent performance regressions; encapsulation should be respected in the GenericBinder rather than using type casts to access private members; the lifecycle management of AbortController in DataContext needs to be restored to prevent resource leaks; and the effect type definition should be updated to support cleanup functions.


batch(() => {
this.updateSignal(normalizedPath);
this.updateSignal(normalizedPath);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The removal of the batch wrapper in notifySignals and notifyAllSignals is a significant change that may impact performance and consistency. Batching ensures that multiple signal updates are processed as a single atomic operation, which is essential to avoid redundant re-computations of derived signals and multiple re-renders of components. Since the core is now framework-agnostic, the FrameworkSignal interface should be extended to include a batch method (e.g., batch: (fn: () => void) => void) so that each framework implementation can provide its own optimization.

} else if ((bindingRef.current as unknown as {context: ComponentContext}).context !== context) {
bindingRef.current = new GenericBinder<Props, 'react'>(context, api.schema);
} else if (
(bindingRef.current as unknown as {context: ComponentContext<'react'>}).context !== context
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Accessing the private context property of GenericBinder via a type cast to unknown is a violation of encapsulation. This makes the code fragile and dependent on internal implementation details. It would be better to expose a public getter for the context or a method like hasContext(context: ComponentContext<SK>): boolean on the GenericBinder class.

Comment on lines +207 to 210
return this.frameworkSignal.isSignal(result)
? result
: this.frameworkSignal.wrap(result);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

In the branch for function calls with no arguments, the AbortController created for the function execution is no longer being aborted when the signal is disposed. The previous implementation (see line 206 in the LEFT side) attached an unsubscribe method to the signal to handle this. Without a similar mechanism or an effect to manage the controller's lifecycle, asynchronous operations started by the function will leak when the context is disposed.

wrap: <T>(val: T) => new Signal<T>(val),
unwrap: <T>(val: Signal<T>) => val.value,
set: <T>(signal: Signal<T>, value: T) => (signal.value = value),
effect: (fn: () => void) => effect(fn),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The type definition for the effect parameter fn is () => void, but the implementation in data-context.ts (e.g., line 227) passes a function that returns a cleanup function. To avoid type errors and ensure proper resource management, the FrameworkSignal interface and this implementation should be updated to support cleanup functions: fn: () => void | (() => void).

Suggested change
effect: (fn: () => void) => effect(fn),
effect: (fn: () => void | (() => void)) => effect(fn),

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

2 participants