State
Reactive value container. When the value changes, all listeners are notified.
ReadableState<T>
A read-only view of a State<T>. Exposes only get(listener?) — no set, reset, or addListener. Use it when you want to pass a state to a consumer that should read but not mutate it.
export type ReadableState<T> = {
readonly _isState: true;
get(listener?: ValueListener<T>): T;
};The _isState: true discriminant lets runtime code and type guards distinguish a ReadableState from a plain value.
import type { ReadableState } from "@domphy/core"
function display(count: ReadableState<number>) {
return { p: (l) => `Count: ${count.get(l)}` }
}ReadableState<T> is exported as a named type from @domphy/core. State<T> satisfies ReadableState<T> — any State can be passed where a ReadableState is expected. toState() also accepts ReadableState<T> as input (returns it as-is).
import { toState } from "@domphy/core"
const count = toState(0)
count.get() // 0
count.set(1) // notify all listeners
count.get() // 1
count.reset() // back to 0Create a State with toState() from @domphy/core. The toState() function is documented in the Utilities page.
Methods
get(listener?)
Returns the current value. If a listener is provided, subscribes it to future changes.
const value = count.get()
// With listener — auto-subscribe
const value = count.get(listener)set(newValue)
Updates the value and notifies all listeners.
count.set(5)reset()
Resets the value to initialValue.
const filter = toState("all")
filter.set("active")
filter.reset()
filter.get() // "all"addListener(listener)
Subscribes a listener to value changes. Returns a release function.
const release = count.addListener((value) => {
console.log(value)
})
// Unsubscribe
release()Reactive children
Pass a function as children to make an element reactive:
const count = toState(0)
const node: DomphyElement = {
p: (listener) => `Count: ${count.get(listener)}`
// ↑ subscribes automatically
}When count.set() is called, the element re-renders automatically.
initialValue
The value passed to the constructor. Used by reset().
const count = toState(0)
count.initialValue // 0ValueOrState<T>
A union type accepted by patch props and element attributes that can be either a plain value, a reactive State<T>, or a read-only ReadableState<T>.
export type ValueOrState<T> = T | State<T> | ReadableState<T>;Use it in function signatures when a prop should accept both static values and reactive states:
import type { ValueOrState } from "@domphy/core"
function myPatch(open: ValueOrState<boolean>): PartialElement {
return {
ariaExpanded: typeof open === "object" && open._isState
? (l) => (open as ReadableState<boolean>).get(l)
: open,
}
}In practice most patch props accept ValueOrState<T> so callers can pass true / false or a toState(false) interchangeably.
RecordState<T>
Per-key reactive record. Unlike State<Record<...>> which notifies all listeners on every change, RecordState notifies only listeners for the specific key that changed.
import { RecordState } from "@domphy/core"
const form = new RecordState({ name: "", age: 0 })Methods
get(key, listener?)
Read a field value. Registers listener (or the active computed/effect collector) for the given key only.
const name = form.get("name") // untracked read
const nameEl = { span: (l) => form.get("name", l) } // reactive — updates when name changesset(key, value)
Write a field. Notifies only listeners subscribed to that key.
form.set("name", "Alice")
form.set("age", 30)addListener(key, fn) / removeListener(key, fn)
Manually subscribe/unsubscribe a callback for a specific key. addListener returns an unsubscribe function.
reset(key)
Restores a field to its initialRecord value.
form.reset("name") // back to ""initialRecord
The original record passed to the constructor. Read-only reference, used by reset().
_dispose()
Removes all listeners from all notifiers. Call in _onRemove if you create a RecordState inside a component.
When to use
Prefer RecordState over a single State<Record<...>> when the record has multiple frequently-changing fields and different UI elements subscribe to different fields. Each setter triggers only the relevant listeners instead of all of them.