A counter
The smallest useful thing: a signal, a derived value, three handlers. The markup is static; only the numbers change. This is the WebAssembly path — the same counter generated as plain JavaScript is Counter, no WASM.
Double: 0
How it works
State
count := reactive.NewSignal(0)
doubled := reactive.Computed(func() int { return count.Get() * 2 })NewSignal creates the state. Computed derives doubled from it — calling count.Get() inside the function is what registers the dependency, so it recomputes whenever count changes.
Hydrating the markup
reactive.Hydrate("#counter", func(root wasmwrap.Element) {
reactive.BindText(root.Find("[data-count]"), func() string {
return strconv.Itoa(count.Get())
})
reactive.BindText(root.Find("[data-doubled]"), func() string {
return strconv.Itoa(doubled.Get())
})
})Hydrate attaches behaviour to markup the page already rendered — the demo div above, written by hand. BindText re-runs its function whenever something it reads changes, and writes the result into the element.
The buttons
reactive.On(root.Find("[data-inc]"), "click", func(wasmwrap.Event) {
count.Update(func(v int) int { return v + 1 })
})
reactive.On(root.Find("[data-dec]"), "click", func(wasmwrap.Event) {
count.Update(func(v int) int { return v - 1 })
})
reactive.On(root.Find("[data-reset]"), "click", func(wasmwrap.Event) {
count.Set(0)
})On is el.On plus automatic cleanup inside an effect. Update reads and writes atomically: increment, decrement and reset each change the signal, and both bindings re-run.
Full source
count := reactive.NewSignal(0)
doubled := reactive.Computed(func() int { return count.Get() * 2 })
reactive.Hydrate("#counter", func(root wasmwrap.Element) {
reactive.BindText(root.Find("[data-count]"), func() string {
return strconv.Itoa(count.Get())
})
reactive.BindText(root.Find("[data-doubled]"), func() string {
return strconv.Itoa(doubled.Get())
})
reactive.On(root.Find("[data-inc]"), "click", func(wasmwrap.Event) {
count.Update(func(v int) int { return v + 1 })
})
reactive.On(root.Find("[data-dec]"), "click", func(wasmwrap.Event) {
count.Update(func(v int) int { return v - 1 })
})
reactive.On(root.Find("[data-reset]"), "click", func(wasmwrap.Event) {
count.Set(0)
})
})How this demo runs
scripts/demo.go is compiled to demo.wasm by CompileFrom and loaded by WasmScript — see Scripts & WebAssembly. Without WebAssembly the page still renders; only the numbers stay put.