选择框
Select 组件让你可以创建选项列表供用户选择。
介绍
选择框 (Select) 是一个 UI 元素,为用户提供一个选项列表以供选择。
MUI Base 的 Select 组件取代了原生的 HTML <select>
标签。它还包括 Option 组件用于创建列表中的选项,以及 Option Group 用于对这些选项进行分组。
组件
import { Select } from '@mui/base/Select';
import { Option } from '@mui/base/Option';
以下演示展示了如何使用多个 Option 组件创建和样式化 Select 组件
表单提交
在 Select 中选择的值可以使用标准的 HTML 表单提交到服务器。
你可以自定义此隐藏输入框的值——请参阅对象值部分了解详细信息。
TypeScript 注意事项
Select 的 props 是泛型的。由于 TypeScript 的限制,当将组件包装在 forwardRef
(或其他高阶组件)中时,可能会导致意外行为。
在这种情况下,泛型参数将默认为 unknown
,并且类型建议将不完整。为了避免这种情况,你可以手动将结果组件强制转换为正确的类型
const CustomSelect = React.forwardRef(function CustomSelect<TValue>(
props: SelectProps<TValue>,
ref: React.ForwardedRef<HTMLUListElement>,
) {
// ...your code here...
return <Select {...props} ref={ref} />;
}) as <TValue>(
props: SelectProps<TValue> & React.RefAttributes<HTMLUListElement>,
) => React.JSX.Element;
为了简洁起见,本文档其余部分的演示将不使用 forwardRef
。
多项选择
设置 multiple
属性以允许用户从列表中选择多个选项。与单选模式相反,选项弹出窗口在选择项目后不会关闭,这使用户可以继续选择更多选项。
请注意,在多选模式下,value
属性(和 defaultValue
)是一个数组。
受控 Select
Select 可以用作非受控或受控组件。
选定值10
使用 value
属性设置受控 Select 的值。非受控组件接受 defaultValue
,可用于设置初始值。要取消选择所有值,请将 null
传递给相应的属性。
对象值
Select 组件可以与非字符串值一起使用
选定的角色
{ "name": "Frodo", "race": "Hobbit" }
如果在表单中使用带有对象值的 Select 并将表单内容发布到服务器,则选定的值将转换为 JSON。你可以借助 getSerializedValue
属性更改此行为。
分组选项
import { OptionGroup } from '@mui/base/OptionGroup';
选项可以分组,类似于原生 <select>
元素的工作方式。与原生 <select>
不同,组可以嵌套。
以下演示展示了如何使用 Option Group 组件对 Option 进行分组
解剖
Select 组件由根 <button>
以及包含 <ul>
的 <div>
组成,该 <div>
位于 Popup 内。Option 渲染为 <li>
,而 Option Group 渲染一个 <ul>
,其中包含一个表示其标签的 <li>
。
<button class="base-Select-root" type="button">Open</button>
<div class="base-Select-popup">
<ul class="base-Select-listbox">
<li class="base-Option-root">Option one</li>
<li class="base-Option-root">Option two</li>
</ul>
</div>
自定义结构
使用 slots
属性覆盖根或任何其他内部 slot
<Select slots={{ root: 'div', listbox: 'ol' }} />
使用 slotProps
属性将自定义 props 传递给内部 slot。以下代码片段将名为 my-listbox
的 CSS 类应用于 listbox slot
<Select slotProps={{ listbox: { className: 'my-listbox' } }} />
Portals
默认情况下,Select 的弹出窗口在 Portal 中渲染并附加到 DOM 的底部。要改为在定义组件的位置渲染弹出窗口,请覆盖底层 Popup 的 disablePortal
属性,如下所示
<Select slotProps={{ popup: { disablePortal: true } }} />
过渡效果
Select 组件支持 Transitions API,因此可以为 Listbox 的出现和消失添加动画效果。为此,请覆盖 Select 的 Listbox slot,并使用过渡组件(CssTransition、CssAnimation 或自定义构建的组件)包装它。
与 TypeScript 一起使用
在 TypeScript 中,你可以将 slots.root
中使用的自定义组件类型指定为非样式化组件的泛型参数。这样,你可以安全地直接在组件上提供自定义根的 props
<Select<typeof CustomComponent> slots={{ root: CustomComponent }} customProp />
这同样适用于特定于自定义原始元素的 props
<Select<'button'> slots={{ root: 'button' }} onClick={() => {}} />
Hook
import { useSelect } from '@mui/base/useSelect';
useSelect
hook 允许你将 Select 的功能应用于完全自定义的组件。它返回要放置在自定义组件上的 props,以及表示组件内部状态的字段。
Hook 不支持 slot props,但它们支持 自定义 props。
以下示例显示了使用 hook 构建的选择框。请注意,此组件不包含任何内置类。与预构建的组件对应项相比,生成的 HTML 小得多,因为未应用类名。
性能
useOption
hook 监听由父 Select 组件设置的上下文中的更改。此上下文在每次高亮显示项目时都会更改。通常,这不应该是一个问题,但是,当你的选择框有数百个选项时,你可能会注意到它不是很灵敏,因为每次高亮显示更改时,都会重新渲染每个选项。
为了通过防止不必要地渲染选项来提高性能,你可以创建一个包装选项的组件。在此组件内部,调用 useOptionContextStabilizer
并使用 hook 结果中的值创建 ListContext
const StableOption = React.forwardRef(function StableOption<OptionValue>(
props: OptionProps<OptionValue>,
ref: React.ForwardedRef<Element>,
) {
const { contextValue } = useOptionContextStabilizer(props.value);
return (
<ListContext.Provider value={contextValue}>
<Option {...props} ref={ref} />
</ListContext.Provider>
);
});
useOptionContextStabilizer
hook 确保仅在选项状态更新时才更改上下文值。
自定义
选定值外观
你可以通过为 renderValue
属性提供一个函数来自定义选定值显示的外观。此函数返回的元素将呈现在 Select 的按钮内部。
选项外观
选项不必是纯字符串。你可以包含自定义元素以在 listbox 内部渲染。