跳到正文

响应式

组件函数在窗口(或父节点)挂载时运行一次。信号会更新依赖它的文本和属性,而不重建整棵树。

#响应式状态

用 ui.CreateSignal 创建信号。在文本和属性中读取它:ui.Text("Count: ", count())、.Width(width())。事件处理函数会自动批量写入。

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() 只是快照。之后需要保持更新时请再次读取信号。也可以把信号当作子节点:ui.Text("Count: ", count) 会保留前缀,只更新数字。

自定义组件接受普通的 int、string 和结构体参数。LineItem(product, quantity()) 会继续订阅 quantity。

#派生状态

用 ui.CreateMemo 从其他信号计算值。

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) }),
	)
}

#副作用与清理

用 ui.CreateEffect 处理树外的工作,例如订阅或窗口标题。它在挂载后运行,并在读取过的值变化时再次运行。ui.OnCleanup 在下次执行前以及组件卸载时运行。

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")
			}
		}),
	)
}

直接更新文本和属性。不要在 effect 里重建界面。

#批量更新

点击处理函数已经会批量写入。在其他地方用 ui.Batch 包住相关写入,让观察者只看到一次结果。ui.Untrack 读取信号但不订阅。在 UI goroutine 上读写信号;后台结果用 ui.Async 或 native.Dispatch 送回。

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),
	)
}