Reactivity
A component function runs once when its window (or parent) mounts. Signals keep text and properties up to date without rebuilding the whole tree.
#Reactive state
Create a signal with ui.CreateSignal. Read it in text and properties: ui.Text("Count: ", count()), .Width(width()). Event handlers batch writes automatically.
func TextExample() *ui.Element {
count, setCount := ui.CreateSignal(0)
return ui.View().Children(
ui.Text(
"Count: ",
count(),
).FontSize(20),
ui.Button().Child("Increment").OnClick(func() { setCount(count() + 1) }),
)
}initial := count() is a snapshot. Read the signal again later when the value should stay live. You can also pass a signal as a child: ui.Text("Count: ", count) keeps the prefix and updates only the number.
Custom components take ordinary int, string, and struct arguments. LineItem(product, quantity()) stays subscribed to quantity.
#Derived state
Use ui.CreateMemo for a value computed from other signals.
func DerivedExample() *ui.Element {
count, setCount := ui.CreateSignal(0)
doubled := ui.CreateMemo(func() int { return count() * 2 })
return ui.View().Children(
ui.Text("Doubled: ", doubled()),
ui.Button().Child("Increment").OnClick(func() { setCount(count() + 1) }),
)
}#Effects and cleanup
Use ui.CreateEffect for work outside the tree, such as a subscription or a window title. It runs after mount and again when a value it read changes. ui.OnCleanup runs before the next execution and when the component goes away.
func EffectExample() *ui.Element {
topic, setTopic := ui.CreateSignal("Inbox")
ui.CreateEffect(func() {
current := topic()
log.Print("Subscribe: ", current)
ui.OnCleanup(func() { log.Print("Unsubscribe: ", current) })
})
return ui.View().Children(
ui.Text("Watching: ", topic()),
ui.Button().Child("Switch topic").OnClick(func() {
if topic() == "Inbox" {
setTopic("Archive")
} else {
setTopic("Inbox")
}
}),
)
}Update text and properties directly. Do not rebuild UI inside an effect.
#Batched updates
Click handlers already batch writes. Wrap related writes elsewhere in ui.Batch so observers see one result. ui.Untrack reads a signal without subscribing. Read and write signals on the UI goroutine; use ui.Async or native.Dispatch to deliver background results.
func BatchedExample() *ui.Element {
first, setFirst := ui.CreateSignal("Ada")
last, setLast := ui.CreateSignal("Lovelace")
ui.CreateEffect(func() { log.Print(first() + " " + last()) })
applyProfile := func() {
ui.Batch(func() {
setFirst("Grace")
setLast("Hopper")
})
}
return ui.View().Children(
ui.Text(first(), " ", last()),
ui.Button().Child("Apply profile").OnClick(applyProfile),
)
}