function / @typed/ui/index

Dom.chainEvent

Combines a user handler with component-required behavior for one native event.

Learn this UI primitive →

Package version
1.0.0-beta.7
Category
Event composition
Since
1.0.0
Member of
Dom

Import

import { Dom } from "@typed/ui/index";

Access Dom.chainEvent through the imported Dom export. Its declaration below describes the member.

This public exposure is a re-export. Its import path is supported; declaration documentation is shared with the other public exposures below.

Signatures

export declare function chainEvent<Ev extends Event, E1 = never, R1 = never, E2 = never, R2 = never>(user: EventHandlerInput<Ev, E1, R1>, internal: EventHandlerInput<Ev, E2, R2>): EventHandler.EventHandler<Ev, E1 | E2, R1 | R2> | undefined;

Why

Component hosts must combine user and required behavior without hiding the precise Effect boundary. User callback invocation constructs the user Effect first. Internal callback invocation may then construct an internal Effect; only execution of that returned Effect is sequenced after the user Effect.

Ownership and lifetime

The returned EventHandler owns no listener by itself. The rendering Scope installs it and removes it; once and AbortSignal state are honored for each input independently. Capture and passive options are conservatively merged.

DOM behavior

The user handler receives a retained Proxy around the browser event so a preventDefault() call made while its Effect runs can still cancel the internal step. The proxy is not identity-equal to the native event. Its properties—including currentTarget—are read from that native event, so currentTarget becomes null after browser dispatch exactly as it normally does. The internal handler receives the original native event, never the proxy.

Invocation and Effect sequencing

userHandler.handler(proxy) is called first to construct userEffect. If that invocation has already called preventDefault()—for example through an EventHandler option—the internal handler is not invoked. Otherwise, internalHandler.handler(nativeEvent) is called immediately to construct internalEffect, before userEffect runs. A void-returning internal callback therefore performs its callback body eagerly and cannot be undone by a later preventDefault() inside userEffect. The composed Effect runs userEffect, checks tracked default prevention again, and only then runs a returned internalEffect when it is still allowed.

Examples

import { chainEvent } from "@typed/ui/Dom/Events"
import { EventHandler } from "@typed/template"
import { Effect } from "effect"

const calls: Array<string> = []
const handler = chainEvent(
  EventHandler.make((event: MouseEvent) =>
    Effect.sync(() => {
      calls.push("user effect")
      event.preventDefault()
    })),
  EventHandler.make(() => {
    calls.push("internal callback") // runs while constructing the Effect
    return Effect.sync(() => calls.push("internal effect"))
  })
)!

const program = handler.handler(new MouseEvent("click", { cancelable: true }))
// calls is now ["internal callback"]
await Effect.runPromise(program)
// calls is ["internal callback", "user effect"];
// the returned internal Effect was gated by preventDefault.

Other public imports

These import paths expose the same declaration. Each page retains its own public name and signature.

Source