Interface
Interfaces with Ajo
Plain functions, generator components and explicit local state.
A view is a function
Stateless components receive arguments and return JSX. Use class for CSS classes, string values for inline styles, and set: for DOM properties and event handlers.
Greeting.tsx
export const Greeting = ({ name }: { name: string }) => (
<p class="greeting">Hello, {name}.</p>
)State lives before the loop
A generator body starts once. Values declared before the rendering loop persist. this.next() advances the generator and updates the view.
Counter.tsx
import type { Stateful } from 'ajo'
const Counter: Stateful = function* () {
let count = 0
while (true) yield (
<button set:onclick={() => this.next(() => count++)}>
Count: {count}
</button>
)
}Read fresh arguments
Use for…of this when each render needs current arguments. Give list items stable keys so their component identity follows the data.
Stepper.tsx
import type { Stateful } from 'ajo'
export const Stepper: Stateful<{ step: number }> = function* () {
let count = 0
for (const { step } of this) yield (
<button set:onclick={() => this.next(() => count += step)}>
{count} · add {step}
</button>
)
}Keep cleanup with the component
Attach listeners and requests to this.signal, which aborts when the component ends. Guard browser-only work during server rendering. A reusable clove shares this same host lifecycle.
Inside a stateful component
if (typeof document !== 'undefined' && this.nodeType === 1) {
document.addEventListener('keydown', onKey, {
signal: this.signal,
})
}