Autocomplete
Autocomplete 从 HeroUI v2 到 v3 的迁移指南。
在 v3 中,v2 的 Autocomplete 可以迁移为 v3 Autocomplete 或 v3 ComboBox,取决于你的使用场景。请参阅下文「何时使用 Autocomplete 与 ComboBox」。本指南只关注从 HeroUI v2 的迁移。
何时使用 Autocomplete 与 ComboBox
v3 提供两个组件来替代 v2 的 Autocomplete:
| 组件 | 适用场景 | 输入方式 | 底层实现 |
|---|---|---|---|
| Autocomplete | 选择器式交互,Popover 内带搜索 / 筛选字段 | 按钮触发器展示已选值;Popover 内为搜索输入 | React Aria Select + Autocomplete |
| ComboBox | 在可见输入框中输入以筛选下拉列表 | 内联文本输入与下拉触发器 | React Aria ComboBox |
使用 Autocomplete:用户从预定义列表中选择,且搜索字段应出现在下拉内(类似可搜索的选择器)。
使用 ComboBox:用户在可见输入框中直接输入以筛选或搜索选项(行为最接近 v2)。
结构变化
在 v2 中,Autocomplete 是内部包装 Input 的单一组件:
import { Autocomplete, AutocompleteItem } from "@heroui/react";
export default function App() {
return (
<Autocomplete label="Select an animal">
<AutocompleteItem key="cat">Cat</AutocompleteItem>
<AutocompleteItem key="dog">Dog</AutocompleteItem>
</Autocomplete>
);
}迁移到 v3 ComboBox(最接近 v2)
ComboBox 提供与 v2 Autocomplete 最接近的体验:内联文本输入并随输入筛选选项。
import { ComboBox, Input, Label, ListBox } from "@heroui/react";
export default function App() {
return (
<ComboBox>
<Label>Select an animal</Label>
<ComboBox.InputGroup>
<Input placeholder="Search animals..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
</ComboBox>
);
}迁移到 v3 Autocomplete(可搜索的选择器)
Autocomplete 提供选择器式触发器,并在 Popover 内提供搜索 / 筛选字段:
import { Autocomplete, Label, SearchField, ListBox, useFilter } from "@heroui/react";
export default function App() {
const { contains } = useFilter({ sensitivity: "base" });
return (
<Autocomplete>
<Label>Select an animal</Label>
<Autocomplete.Trigger>
<Autocomplete.Value />
<Autocomplete.ClearButton />
<Autocomplete.Indicator />
</Autocomplete.Trigger>
<Autocomplete.Popover>
<Autocomplete.Filter filter={contains}>
<SearchField>
<SearchField.Group>
<SearchField.SearchIcon />
<SearchField.Input placeholder="Search..." />
</SearchField.Group>
</SearchField>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Autocomplete.Filter>
</Autocomplete.Popover>
</Autocomplete>
);
}关键变化
1. 两个替代组件
v2: Autocomplete(单一组件)
v3: ComboBox(内联输入 + 下拉)或 Autocomplete(选择器式 + Popover 内搜索)
2. 组件结构
v2: 单一组件,内部包含 Input
v3 ComboBox: 复合组件(ComboBox.InputGroup、ComboBox.Trigger、ComboBox.Popover)
v3 Autocomplete: 复合组件(Autocomplete.Trigger、Autocomplete.Value、Autocomplete.ClearButton、Autocomplete.Indicator、Autocomplete.Popover、Autocomplete.Filter)
3. 菜单项组件
v2: AutocompleteItem、AutocompleteSection
v3: ListBox.Item、ListBox.Section(来自 ListBox)
4. 菜单项标识
v2: React 的 key 同时用于列表协调与选择时的项标识。
v3: 在 ListBox.Item 上使用 id 与 textValue(状态与无障碍);列表中的项仍保留 React 的 key。
5. Autocomplete 专用子组件(v3)
v3 Autocomplete 引入 v2 中不存在的子组件:
| 子组件 | 用途 |
|---|---|
Autocomplete.Trigger | 打开 Popover 的按钮组 |
Autocomplete.Value | 显示当前选中值或占位符 |
Autocomplete.ClearButton | 清除当前选择 |
Autocomplete.Indicator | 下拉箭头图标(打开时旋转) |
Autocomplete.Popover | 下拉 Popover 容器 |
Autocomplete.Filter | 包裹 SearchField 与 ListBox 以启用筛选;接受 filter 函数、inputValue、onInputChange |
6. SearchField 集成
v3 Autocomplete 在 Autocomplete.Filter 内使用 SearchField,作为 Popover 中的搜索输入:
<Autocomplete.Filter filter={contains}>
<SearchField>
<SearchField.Group>
<SearchField.SearchIcon />
<SearchField.Input placeholder="Search..." />
</SearchField.Group>
</SearchField>
<ListBox>...</ListBox>
</Autocomplete.Filter>SearchField 还有子组件:SearchField.Group、SearchField.Input、SearchField.SearchIcon、SearchField.ClearButton。
7. useFilter Hook
v3 Autocomplete 使用 useFilter hook(来自 React Aria,由 @heroui/react 再导出)提供带区域设置感知的筛选函数:
import { useFilter } from "@heroui/react";
const { contains } = useFilter({ sensitivity: "base" });
// 传给 Autocomplete.Filter
<Autocomplete.Filter filter={contains}>
...
</Autocomplete.Filter>该 hook 返回 contains、startsWith、endsWith。sensitivity 控制区域设置感知匹配("base"、"accent"、"case"、"variant")。
8. Prop 变更(ComboBox)
| v2 prop | v3 位置 | 说明 |
|---|---|---|
| — | id(在 ListBox.Item 上) | 状态用的项标识 |
| — | textValue(在 ListBox.Item 上) | 无障碍(提前输入) |
label | Label | 使用 Label 组件 |
description | Description | 使用 Description 组件 |
placeholder | Input | 写在 Input 的 placeholder |
selectedKey、onSelectionChange、inputValue、onInputChange | ComboBox | 与 v2 相同 |
allowsCustomValue、allowsEmptyCollection、defaultFilter | ComboBox | 与 v2 相同 |
disabledKeys、isDisabled、isRequired、isInvalid、name | ComboBox | 与 v2 相同 |
menuTrigger | ComboBox | "focus"(默认)、"input" 或 "manual" |
variant、color、size、radius | — | 已移除(请使用 Tailwind CSS) |
labelPlacement | — | 已移除(请自行摆放 Label) |
startContent、endContent | — | 加到 Input 或 InputGroup |
selectorIcon、clearIcon | — | 自定义 ComboBox.Trigger 或手动实现 |
isClearable | — | 手动实现 |
showScrollIndicators | — | 已移除 |
classNames | — | 在各部件上使用 className |
popoverProps、listboxProps、inputProps | — | 直接在对应子组件上配置 |
scrollShadowProps、scrollRef | — | 已移除 |
selectorButtonProps、clearButtonProps、disableSelectorIconRotation | — | 已移除 |
isReadOnly | ComboBox | ComboBox 上的 isReadOnly prop |
fullWidth | ComboBox | ComboBox 上的 fullWidth prop |
isVirtualized、maxListboxHeight、itemHeight | — | 已移除 |
onClose、onClear | — | 改用其他事件处理函数 |
validationBehavior、validate | ComboBox | 写在 ComboBox 上的 prop |
9. Prop 变更(Autocomplete)
| v2 prop | v3 位置 | 说明 |
|---|---|---|
| — | id(在 ListBox.Item 上) | 状态用的项标识 |
| — | textValue(在 ListBox.Item 上) | 无障碍(提前输入) |
label | Label | 使用 Label 组件 |
description | Description | 使用 Description 组件 |
placeholder | Autocomplete | 根组件上的 placeholder prop |
selectedKey / onSelectionChange | value / onChange | Autocomplete 上已重命名 |
selectionMode | Autocomplete | "single"(默认)或 "multiple" |
inputValue、onInputChange | Autocomplete.Filter | 写在 Filter 上的 inputValue 与 onInputChange |
disabledKeys、isDisabled、isRequired、isInvalid、name | Autocomplete | 与 v2 相同 |
variant、color、size、radius | — | 已移除(请使用 Tailwind CSS);variant 支持 "primary" / "secondary" |
isClearable | Autocomplete.ClearButton | 内置子组件 |
selectorIcon | Autocomplete.Indicator | 将自定义图标作为 children 传入 |
onClear | Autocomplete | 根组件上的 onClear prop |
fullWidth | Autocomplete | 根组件上的 fullWidth prop |
classNames | — | 在各部件上使用 className |
popoverProps、listboxProps、inputProps | — | 直接在对应子组件上配置 |
迁移示例
受控选择(ComboBox)
import { useState } from "react";
const [selectedKey, setSelectedKey] = useState("cat");
<Autocomplete
selectedKey={selectedKey}
onSelectionChange={setSelectedKey}
label="Animal"
>
<AutocompleteItem key="cat">Cat</AutocompleteItem>
<AutocompleteItem key="dog">Dog</AutocompleteItem>
</Autocomplete>import { useState } from "react";
import type { Key } from "@heroui/react";
const [selectedKey, setSelectedKey] = useState<Key | null>("cat");
<ComboBox
selectedKey={selectedKey}
onSelectionChange={setSelectedKey}
>
<Label>Animal</Label>
<ComboBox.InputGroup>
<Input placeholder="Search animals..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
</ComboBox>受控选择(Autocomplete)
import { useState } from "react";
const [selectedKey, setSelectedKey] = useState("cat");
<Autocomplete
selectedKey={selectedKey}
onSelectionChange={setSelectedKey}
label="Animal"
>
<AutocompleteItem key="cat">Cat</AutocompleteItem>
<AutocompleteItem key="dog">Dog</AutocompleteItem>
</Autocomplete>import { useState } from "react";
import type { Key } from "@heroui/react";
import { Autocomplete, Label, SearchField, ListBox, useFilter } from "@heroui/react";
const [value, setValue] = useState<Key | Key[] | null>("cat");
const { contains } = useFilter({ sensitivity: "base" });
<Autocomplete value={value} onChange={setValue}>
<Label>Animal</Label>
<Autocomplete.Trigger>
<Autocomplete.Value />
<Autocomplete.ClearButton />
<Autocomplete.Indicator />
</Autocomplete.Trigger>
<Autocomplete.Popover>
<Autocomplete.Filter filter={contains}>
<SearchField>
<SearchField.Group>
<SearchField.SearchIcon />
<SearchField.Input placeholder="Search..." />
</SearchField.Group>
</SearchField>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
<Label>Cat</Label>
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
<Label>Dog</Label>
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Autocomplete.Filter>
</Autocomplete.Popover>
</Autocomplete>带分组
<Autocomplete label="Country">
<AutocompleteSection title="North America">
<AutocompleteItem key="usa">United States</AutocompleteItem>
<AutocompleteItem key="canada">Canada</AutocompleteItem>
</AutocompleteSection>
<AutocompleteSection title="Europe">
<AutocompleteItem key="uk">United Kingdom</AutocompleteItem>
</AutocompleteSection>
</Autocomplete>import { Header, Separator } from "@heroui/react";
<ComboBox>
<Label>Country</Label>
<ComboBox.InputGroup>
<Input placeholder="Search countries..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Section>
<Header>North America</Header>
<ListBox.Item id="usa" textValue="United States">
United States
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="canada" textValue="Canada">
Canada
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox.Section>
<Separator />
<ListBox.Section>
<Header>Europe</Header>
<ListBox.Item id="uk" textValue="United Kingdom">
United Kingdom
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox.Section>
</ListBox>
</ComboBox.Popover>
</ComboBox>使用 useFilter(Autocomplete)
{/* v2 在内部处理筛选 */}
<Autocomplete label="Animal">
<AutocompleteItem key="cat">Cat</AutocompleteItem>
<AutocompleteItem key="dog">Dog</AutocompleteItem>
</Autocomplete>import { Autocomplete, Label, SearchField, ListBox, useFilter } from "@heroui/react";
function FilterExample() {
const { contains } = useFilter({ sensitivity: "base" });
return (
<Autocomplete>
<Label>Animal</Label>
<Autocomplete.Trigger>
<Autocomplete.Value />
<Autocomplete.ClearButton />
<Autocomplete.Indicator />
</Autocomplete.Trigger>
<Autocomplete.Popover>
<Autocomplete.Filter filter={contains}>
<SearchField>
<SearchField.Group>
<SearchField.SearchIcon />
<SearchField.Input placeholder="Search..." />
</SearchField.Group>
</SearchField>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
<Label>Cat</Label>
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
<Label>Dog</Label>
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Autocomplete.Filter>
</Autocomplete.Popover>
</Autocomplete>
);
}表单校验
{/* Required field */}
<Autocomplete label="Animal" isRequired>
<AutocompleteItem key="cat">Cat</AutocompleteItem>
</Autocomplete>
{/* With error message */}
<Autocomplete
label="Animal"
isInvalid
errorMessage="Please select an animal"
>
<AutocompleteItem key="cat">Cat</AutocompleteItem>
</Autocomplete>import { FieldError, Form } from "@heroui/react";
{/* Required field */}
<Form>
<ComboBox isRequired name="animal">
<Label>Animal</Label>
<ComboBox.InputGroup>
<Input placeholder="Search animals..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
<FieldError />
</ComboBox>
</Form>
{/* With error message */}
<ComboBox isInvalid>
<Label>Animal</Label>
<ComboBox.InputGroup>
<Input placeholder="Search animals..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
<FieldError>Please select an animal</FieldError>
</ComboBox>组件剖析
ComboBox 剖析
ComboBox (Root)
├── Label
├── ComboBox.InputGroup
│ ├── Input
│ └── ComboBox.Trigger
├── Description (optional)
├── ComboBox.Popover
│ └── ListBox
│ ├── ListBox.Item
│ │ ├── [Content]
│ │ └── ListBox.ItemIndicator (optional)
│ └── ListBox.Section (optional)
│ ├── Header
│ └── ListBox.Item
└── FieldError (optional)Autocomplete 剖析
Autocomplete (Root)
├── Label
├── Autocomplete.Trigger
│ ├── Autocomplete.Value
│ ├── Autocomplete.ClearButton
│ └── Autocomplete.Indicator
├── Description (optional)
├── Autocomplete.Popover
│ └── Autocomplete.Filter
│ ├── SearchField
│ │ └── SearchField.Group
│ │ ├── SearchField.SearchIcon
│ │ └── SearchField.Input
│ └── ListBox
│ ├── ListBox.Item
│ │ ├── [Content]
│ │ └── ListBox.ItemIndicator (optional)
│ └── ListBox.Section (optional)
│ ├── Header
│ └── ListBox.Item
└── FieldError (optional)总结
- 两个替代组件:v2
Autocomplete可迁移为 v3ComboBox(内联输入)或 v3Autocomplete(Popover 内可筛选的选择器)。 - 组件结构:由带 prop 的单一组件变为带显式子节点的复合组件。
- 菜单项组件:
AutocompleteItem→ListBox.Item;AutocompleteSection→ListBox.Section。 - 菜单项标识:在项上使用
id与textValue;列表协调仍用 React 的key。 - 输入:v3 ComboBox 需要显式
Input;v3 Autocomplete 在Autocomplete.Filter内使用SearchField。 - 标签 / 描述:对应 prop 拆为独立的
Label与Description组件。 - 筛选:v3 Autocomplete 通过
Autocomplete.Filter与useFilter做区域设置感知筛选;v3 ComboBox 使用defaultFilterprop。 - 清除按钮:v3 Autocomplete 内置
Autocomplete.ClearButton;v3 ComboBox 需手动实现。 - 选中值展示:v3 Autocomplete 用
Autocomplete.Value展示选中值,并支持渲染 prop。 - 样式类 prop 已移除:
color、size、radius已移除(请使用 Tailwind CSS);variant现支持"primary"/"secondary"。 - classNames 已移除:在各子组件上使用
classNameprop。 - useFilter Hook:v3 新增,提供
contains、startsWith、endsWith用于区域设置感知的文本匹配。