跳到内容
+

选择框

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 表单提交到服务器。

按下 Enter 键开始编辑

你可以自定义此隐藏输入框的值——请参阅对象值部分了解详细信息。

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,并使用过渡组件(CssTransitionCssAnimation 或自定义构建的组件)包装它。

与 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 的按钮内部。

按下 Enter 键开始编辑

选项外观

选项不必是纯字符串。你可以包含自定义元素以在 listbox 内部渲染。

按下 Enter 键开始编辑