Refactor Refs
Refactor Refs
Written for AI agents. See Log Methodology Note below for details.
First of all, lets understand why.
today, references are created as part of the
JayElementcreation, which is done as part of the render functionrender: (vs: ViewState) => JayElement.However, we have a cycling problem with
JayComponent, that requiresrefsto run the initial function to create theViewStatewhich is used for the render function which creates therefs...We have two implementations of
Refs- for main and sandbox, and those are differentWe have 4 types of
Refsfor each environment - HTML element, collection of HTML Elements Component and collection of components. In effect, we also need to have 2 additional types for conditional HTML Element and conditional componentWe have inconsistencies in declaring
Refs- for the main environment, we declare the dynamic refs. For the sandbox environment, we declare dynamic elements and components
All of the above "smalls" like we need a new pattern
The new Pattern
We want a pattern at which when creating the JayElement, the refs are created first, allowing
the component to be created, generate the ViewState, then render the JayElement.
In essence, it is moving from
export function render(vs: ViewState): JayElement<ViewState> {}
into
export function mkElement(): JayElement<ViewState> {
const refs: Record<string, Ref> = {};
const title = (refs['title'] = ref());
const name = (refs['name'] = ref());
// ... declare refs
return [publicRefsAPI(refs), function render(vs: ViewState): JayElement<ViewState> {}];
}
Using this pattern we will create all refs - static, conditional or collection (denoted dynamic above)
in the first section, transform all to their public API and return both the refs and render.
lets explore the Refs API
private API
looking at element.ts/mkRef before this refactor -
function mkRef(
refName: string,
referenced: HTMLElement | JayComponent<any, any, any>,
updates: updateFunc<any>[],
mounts: MountFunc[],
unmounts: MountFunc[],
isComp: boolean,
) {
let context = currentConstructionContext();
let [ref, update] = context.refManager.mkRef(referenced, context, refName, isComp);
updates.push(update);
if (context.forStaticElements) {
context.refManager.addStaticRef(refName, ref);
} else {
let refManager = context.refManager;
mounts.push(() => refManager.addDynamicRef(refName, ref));
unmounts.push(() => refManager.removeDynamicRef(refName, ref));
}
}
we see that the interaction of the element.ts structure with refs is contained within a simple contract -
- when we have an element, static or dynamic, we create a ref instance for it and add it to the ref manager
- when calling mount or unmount for dynamic refs, we add or remove the reference.
We can turn this function around so that it accepts as a parameter the ref private API, where such an API is
- it has an
updatefunction - it has
mountandunmountfunctions to add and remove dynamic elements, or just add static elements given the creation of refs will be outside of the mkRef function, there is no need for the reference manager
the function can turn into
function mkRef(
ref: ReferencePrivateAPI,
referenced: HTMLElement | JayComponent<any, any, any>,
updates: updateFunc<any>[],
mounts: MountFunc[],
unmounts: MountFunc[],
) {
let context = currentConstructionContext();
updates.push(ref.update);
if (context.forStaticElements) {
ref.mount(referenced);
} else {
mounts.push(() => ref.mount(referenced));
unmounts.push(() => ref.unmount(referenced));
}
}
those, the private API for any type of ref is
interface ReferencePrivateAPI<ViewState> {
updateFunc<ViewState>,
mount(referenced: HTMLElement | JayComponent<any, any, any>),
unmount(referenced: HTMLElement | JayComponent<any, any, any>),
getPublicAPI: ReferencePublicAPI /* will be element or component API, depending on the actual type of the ref */
}
HTMLElement Ref
interface HTMLElementPrivateRef<ViewState> {
addEventListener<E extends Event>(
type: string,
listener: JayEventHandler<E, ViewState, any>,
options?: boolean | AddEventListenerOptions,
): void;
removeEventListener<E extends Event>(
type: string,
listener: JayEventHandler<E, ViewState, any>,
options?: EventListenerOptions | boolean,
): void;
$exec<T>(handler: (elem: Element, viewState: ViewState) => T): Promise<T>;
update(newData: ViewState);
getPublicAPI(): HTMLElementRef;
}
interface HTMLElementRef
extends
GlobalJayEvents<ViewState>,
HTMLElementProxyTarget<ViewState, ElementType>,
HTMLNativeExec<ViewState, ElementType> {}
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.