实践范例:带建议的搜索¶
这个模式有两个槽位:一个持有查询字符串的输入框,以及一个展示「针对某个
目录过滤出的零条或多条建议」的下拉列表。UseMemo 把过滤以查询为键缓存起来,因此
没有触碰查询的重渲染是零成本的 —— 这在「上箭头/下箭头」那类
「建议索引变了但查询没变」的循环里很重要。
原语¶
| 关注点 | API |
|---|---|
| 查询状态 | UseState<string> |
| 过滤缓存 | 以查询为键的 UseMemo<T[]> |
| 下拉列表渲染 | 包裹 VStack 的 Border |
| 生产级替代 | AutoSuggestBox |
| 异步来源 | async-resources |
目录¶
class SearchBox : Component
{
private static readonly string[] Catalog = new[] {
"Account settings", "Accessibility", "Animation",
"Buttons", "Backdrop", "Charts",
"Components", "Commanding", "Context",
"Effects", "Forms", "Hooks",
"Localization", "Navigation", "Persistence",
"Styling", "Testing", "Theming tokens",
};
一个静态数组充当数据源。一旦目录上了网络,真实应用就会改用
async-resources。
过滤¶
// UseMemo on the dependency array means the filter runs only when
// query changes — typing fast doesn't refilter mid-keystroke, and
// a re-render that didn't touch the query is free.
var (query, setQuery) = UseState("");
var suggestions = UseMemo(
() => string.IsNullOrWhiteSpace(query)
? new string[0]
: Catalog.Where(c => c.Contains(query,
System.StringComparison.OrdinalIgnoreCase))
.Take(5).ToArray(),
query);
UseMemo 在不改变 query 的渲染之间缓存过滤后的数组。查询为空的分支返回
空数组,从而让下拉列表关闭 —— 用户清空输入时收起建议面
是必不可少的。
渲染¶
return VStack(8,
TextBox(query, setQuery, placeholderText: "Search topics…")
.AutomationName("Search topics")
.Width(300),
suggestions.Length == 0
? Empty()
: Border(
VStack(2,
suggestions.Select(s =>
TextBlock(s).Padding(8).WithKey(s)).ToArray()
).Background(Theme.SolidBackground)
).WithBorder(Theme.ControlStroke).Width(300)
).Padding(20);

下拉列表是一个包裹 VStack 的 Border —— 与
模态对话框范例相同的条件渲染形态。当
suggestions.Length == 0 时,Empty() 工厂会让下拉列表彻底离开
元素树。
对于生产级的搜索框,请把手写的下拉列表换成
AutoSuggestBox —— 它已经接好了键盘导航、
选择事件与屏幕阅读器契约。这个范例展示的是原语,
以便让组合过程可见。
提示¶
限制建议条数。 这里用 Take(5);目录可以很大,
但下拉列表永远只显示一小把。无上限的列表会
迫使你引入 ScrollView,进而迫使你处理键盘,再进而迫使你做
焦点管理 —— 在常见场景下这些都是应该避免的真实工作量。
UseMemo 是承重的那次调用。 没有它,缓慢的过滤器会在每次渲染时
运行 —— 包括那些由「建议列表键盘导航」触发、并未改变查询的渲染。
有了它,过滤只在查询真正变化时运行。
尽早为异步做规划。 客户端过滤只有一行;网络过滤则需要取消、
防抖和一个错误面板。当目录离开本机时,
async-resources 页给出了升级路径。