Skip to content
Lenso UI

Select

Select 从 HeroUI v2 到 v3 的迁移指南。

完整的 API 参考、样式指南与高级示例请参阅 v3 Select 文档。本指南只关注从 HeroUI v2 的迁移。

结构变化

在 v2 中,Select 通过 prop 构成简单结构:

import { Select, SelectItem } from "@lenso/ui";
export default function App() {  return (    <Select label="Select animal" placeholder="Choose one">      <SelectItem key="cat">Cat</SelectItem>      <SelectItem key="dog">Dog</SelectItem>    </Select>  );}

在 v3 中,Select 需要使用复合组件,并通过 ListBox 渲染菜单项:

import { Select, Label, ListBox } from "@lenso/ui";
export default function App() {  return (    <Select placeholder="Choose one">      <Label>Select animal</Label>      <Select.Trigger>        <Select.Value />        <Select.Indicator />      </Select.Trigger>      <Select.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>      </Select.Popover>    </Select>  );}

关键变化

1. 组件结构

v2: 简单的 Select,子节点为 SelectItem
v3: 复合组件(Select.Trigger、Select.Value、Select.ClearButton、Select.Indicator、Select.Popover)与用于菜单项的 ListBox

2. 菜单项组件

v2: SelectItem、SelectSection
v3: ListBox.Item、ListBox.Section(分组标题使用 Header,分组之间使用 Separator)

3. Prop 变更

v2 propv3 位置说明
selectedKeysvalue从 Set / 数组改为单个值或数组
onSelectionChangeonChange已重命名的事件处理函数
defaultSelectedKeysdefaultValue已重命名的 prop
label—使用 Label 组件
description—使用 Description 组件
errorMessage—使用 FieldError 组件
variantvariant仅保留 primary | secondary
color—已移除(请使用 Tailwind CSS)
size—已移除(请使用 Tailwind CSS)
radius—已移除(请使用 Tailwind CSS)
classNames—在各子组件上使用 className prop
startContent—直接自定义 Select.Trigger
endContent—直接自定义 Select.Trigger
selectorIcon—自定义 Select.Indicator 的 children
isClearableSelect.ClearButton内置子组件;不要在 Select.Trigger 内嵌套 <button>
renderValue—使用 Select.Value 的渲染 prop
labelPlacement—标签始终在外部
isRequiredisRequired仍然可用
disabledKeysdisabledKeys仍然可用
isOpenisOpen新增:受控地控制 Popover 的打开状态
defaultOpendefaultOpen新增:非受控的默认打开状态
onOpenChangeonOpenChange新增:打开状态变化时触发
selectionModeselectionMode仍然可用
disableAnimation—已移除(动画机制已不同)
popoverProps、listboxProps、scrollShadowProps—直接在 Select.Popover、ListBox 等组件上传入 prop

迁移示例

选择

import { useState } from "react";
{/* Single selection */}const [singleValue, setSingleValue] = useState(new Set([]));<Select  selectedKeys={singleValue}  onSelectionChange={setSingleValue}  label="Select animal">  <SelectItem key="cat">Cat</SelectItem>  <SelectItem key="dog">Dog</SelectItem></Select>
{/* Multiple selection */}const [multiValue, setMultiValue] = useState(new Set([]));<Select  selectionMode="multiple"  selectedKeys={multiValue}  onSelectionChange={setMultiValue}  label="Select animals">  <SelectItem key="cat">Cat</SelectItem>  <SelectItem key="dog">Dog</SelectItem></Select>

表单校验

{/* With description */}<Select  label="Select animal"  description="Choose your favorite"  placeholder="Choose one">  <SelectItem key="cat">Cat</SelectItem></Select>
{/* With validation */}<Select  isInvalid  errorMessage="Please select an option"  label="Select animal">  <SelectItem key="cat">Cat</SelectItem></Select>

带分组

import { Select, SelectItem, SelectSection } from "@lenso/ui";
<Select label="Select animal">  <SelectSection title="Mammals">    <SelectItem key="cat">Cat</SelectItem>    <SelectItem key="dog">Dog</SelectItem>  </SelectSection>  <SelectSection title="Birds">    <SelectItem key="eagle">Eagle</SelectItem>    <SelectItem key="parrot">Parrot</SelectItem>  </SelectSection></Select>

受控的打开状态

{/* v2 不支持受控的打开状态 */}<Select label="Select animal">  <SelectItem key="cat">Cat</SelectItem>  <SelectItem key="dog">Dog</SelectItem></Select>

禁用选项

import { Select, SelectItem } from "@lenso/ui";
<Select label="Select animal" disabledKeys={["dog"]}>  <SelectItem key="cat">Cat</SelectItem>  <SelectItem key="dog">Dog</SelectItem>  <SelectItem key="parrot">Parrot</SelectItem></Select>

必填

import { Select, SelectItem } from "@lenso/ui";
<Select isRequired label="Select animal">  <SelectItem key="cat">Cat</SelectItem>  <SelectItem key="dog">Dog</SelectItem></Select>

自定义指示器

<Select selectorIcon={<CustomIcon />} label="Select animal">  <SelectItem key="cat">Cat</SelectItem></Select>

组件剖析

v3 Select 的结构如下:

Select (Root)  ├── Label (optional)  ├── Select.Trigger  │   ├── Select.Value  │   ├── Select.ClearButton(可选)  │   └── Select.Indicator  ├── Description (optional)  ├── Select.Popover  │   └── ListBox  │       ├── ListBox.Item  │       │   ├── Label (optional)  │       │   ├── Description (optional)  │       │   └── ListBox.ItemIndicator  │       ├── ListBox.Section (optional)  │       │   ├── Header (section title)  │       │   └── ListBox.Item  │       └── Separator (optional, between sections)  └── FieldError (optional)

重要说明

菜单项标识

  • v2: React 的 key 同时用于列表协调与选择时的项标识。
  • v3: 在 ListBox.Item 上使用 id(状态 / 焦点)与 textValue(无障碍);列表协调仍使用 React 的 key。

选择值的类型

  • v2: selectedKeys 为 Set<Key> 或数组
  • v3: 单选时 value 为 Key | null,多选时为 Key[]

清除按钮

isClearable prop 已移除。请在 Select.Trigger 内使用 Select.ClearButton。不要在触发器内嵌套 <button> — Select.Trigger 本身已经是 button。

清除控件面向指针操作;触发器同时支持按 Backspace 或 Delete 清除,以便键盘与屏幕阅读器用户使用。

<Select value={value} onChange={setValue}>  <Select.Trigger>    <Select.Value />    <Select.ClearButton />    <Select.Indicator />  </Select.Trigger>  {/* ... */}</Select>

总结

  1. 组件结构:必须使用复合组件(Select.Trigger、Select.Value、Select.ClearButton、Select.Indicator、Select.Popover)。
  2. 菜单项组件:SelectItem → ListBox.Item,SelectSection → ListBox.Section(分组标题用 Header,分组之间用 Separator)。
  3. 标签 / 描述 / 错误:使用独立组件,而不是对应 prop。
  4. 选择相关 prop:selectedKeys / onSelectionChange → value / onChange。
  5. 受控的打开状态:新增 isOpen、defaultOpen、onOpenChange 用于控制 Popover 的打开状态。
  6. 禁用选项:仍支持 disabledKeys 以禁用指定菜单项。
  7. 必填:仍支持 isRequired 标记字段为必填。
  8. 样式:variant 仅保留 primary | secondary;color、size、radius 已移除,请用 Tailwind CSS 扩展。
  9. classNames 已移除:在各子组件上使用 className prop。
  10. 内容类 prop 已移除:startContent、endContent 需通过自定义触发器实现。
  11. 清除按钮:isClearable 已移除,请在 Select.Trigger 内使用 Select.ClearButton(不要嵌套 <button>)。
  12. 自定义值:用 Select.Value 的渲染 prop 替代 renderValue。