Skip to content

实践范例:带建议的搜索

这个模式有两个槽位:一个持有查询字符串的输入框,以及一个展示「针对某个 目录过滤出的零条或多条建议」的下拉列表。UseMemo 把过滤以查询为键缓存起来,因此 没有触碰查询的重渲染是零成本的 —— 这在「上箭头/下箭头」那类 「建议索引变了但查询没变」的循环里很重要。

原语

关注点 API
查询状态 UseState<string>
过滤缓存 以查询为键的 UseMemo<T[]>
下拉列表渲染 包裹 VStackBorder
生产级替代 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);

建议下拉列表

下拉列表是一个包裹 VStackBorder —— 与 模态对话框范例相同的条件渲染形态。当 suggestions.Length == 0 时,Empty() 工厂会让下拉列表彻底离开 元素树。

对于生产级的搜索框,请把手写的下拉列表换成 AutoSuggestBox —— 它已经接好了键盘导航、 选择事件与屏幕阅读器契约。这个范例展示的是原语, 以便让组合过程可见。

提示

限制建议条数。 这里用 Take(5);目录可以很大, 但下拉列表永远只显示一小把。无上限的列表会 迫使你引入 ScrollView,进而迫使你处理键盘,再进而迫使你做 焦点管理 —— 在常见场景下这些都是应该避免的真实工作量。

UseMemo 是承重的那次调用。 没有它,缓慢的过滤器会在每次渲染时 运行 —— 包括那些由「建议列表键盘导航」触发、并未改变查询的渲染。 有了它,过滤只在查询真正变化时运行。

尽早为异步做规划。 客户端过滤只有一行;网络过滤则需要取消、 防抖和一个错误面板。当目录离开本机时, async-resources 页给出了升级路径。

后续阅读

  • 表单 —— 当你需要键盘导航与无障碍对等体时,升级为 AutoSuggestBox
  • 异步资源 —— 带取消能力的网络建议。
  • Hook —— UseMemo 的语义 —— 缓存何时失效、何时 继续存活。
  • 实践范例:主从视图 —— 把搜索框与 匹配记录的列表配对。
  • 实践范例索引 —— 回到图库。