---
localeCode: zh-CN
order: 25
category: 输入类
title: Select 选择器
icon: doc-select
width: 60%
brief: 用户可以通过 Select 选择器从一个选项集合中去选中一个或多个选项,并呈现最终选择结果
---
## 代码演示
### 如何引入
```jsx import
import { Select } from '@douyinfe/semi-ui';
const Option = Select.Option;
```
Select的直接子元素必须为 Option 或者 OptGroup,不允许为其他Element
### 基本使用
每个 Option 标签都必须声明 value 属性,Option 的 children 或 label 将会被渲染至下拉列表中
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => (
<>
>
);
```
### 以数组形式传入 Option
可以直接通过`optionList`传入一个对象数组,每个对象必须包含 value/label 属性(当然其他属性也可以通过此方式传入)
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => {
const list = [
{ value: 'abc', label: '抖音', otherKey:0 },
{ value: 'hotsoon', label: '火山小视频', disabled: true, otherKey: 1 },
{ value: 'jianying', label: '剪映', otherKey: 2 },
{ value: 'toutiao', label: '今日头条', otherKey: 3 },
];
return (
);
};
```
### 多选
配置`multiple`属性,可以支持多选
配置 `maxTagCount` 可以限制已选项展示的数量,超出部分将以+N 的方式展示
配置 `max` 属性可限制最大可选的数量,超出最大限制数量后无法选中,同时会触发`onExceed`回调
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => (
<>
>
);
```
### 分组
分组功能 v0.31.0 后提供
用 OptGroup 进行分组(分组功能仅支持通过jsx方式声明children使用,不支持optionList方式传入)
1. OptGroup必须为Select的直接子元素,不允许有Fragment或DIV等其他元素阻隔
2. 若Select的children需要动态更新,OptGroup上的key也需要进行更新,否则Select无法识别
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => (
);
```
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => {
const data = [
{
label: 'Asia',
children: [
{ value: 'a-1', label: 'China' },
{ value: 'a-2', label: 'Koera' },
]
},
{
label: 'Europe',
children: [
{ value: 'b-1', label: 'Germany' },
{ value: 'b-2', label: 'France' },
]
},
{
label: 'South America',
children: [
{ value: 'c-1', label: 'Peru' },
]
}
];
return (
);
};
```
### 不同尺寸
通过Size控制选择器的大小尺寸: `small` / `default` / `large`
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => (
<>
>
);
```
### 不同校验状态样式
validateStatus: default / warning / error
仅影响背景颜色等样式表现
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => (
<>
>
);
```
### 配置前缀、后缀、清除按钮
- 可以通过`prefix`传入选择框前缀,通过`suffix`传入选择框后缀,可以为文本或者 ReactNode
当 prefix、suffix 传入的内容为文本或者 Icon 时,会自动带上左右间隔,若为自定义 ReactNode,则左右间隔为 0
- 通过`showClear`控制清除按钮是否展示
- 通过`showArrow`控制右侧下拉箭头是否展示
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
import { IconVigoLogo, IconGift } from '@douyinfe/semi-icons';
() => (
<>
}
showClear={true}
>
抖音
火山
剪映
西瓜视频
}
suffix={}
showArrow={false}
>
抖音
火山
剪映
西瓜视频
>
);
```
### 内嵌标签
通过设置`insetLabel`,你可以给 Select 设置 label,可以传入 string 或者 ReactNode
当传入类型为 ReactNode 时,注意要自行处理 label 与文本之间的间隔
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => {
const list = [
{ value: 'abc', label: '抖音' },
{ value: 'hotsoon', label: '火山小视频' },
{ value: 'jianying', label: '剪映' },
{ value: 'toutiao', label: '今日头条' },
];
return (
<>
>
);
};
```
### 在顶部/底部渲染附加项
我们在弹出层顶部、底部分别预留了插槽,当你需要在弹出层中添加自定义 node 时
可以通过`innerBottomSlot`或者`outerBottomSlot`传入,自定义 node 将会被渲染在弹出层底部;可以通过`innerTopSlot`或者`outerTopSlot`传入,自定义 node 将会被渲染在弹出层顶部。
- `innerTopSlot` 和 `innerBottomSlot`将会被渲染在 optionList 内部,当滚动到 optionList 顶部/底部时展现
- `outerTopSlot` 和 `outerBottomSlot`将会被渲染为与 optionList 平级,无论 optionList 是否滚动,都会始终展现
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => {
let selectStyle = { width: 180, margin: 20 };
let innerSlotStyle = {
backgroundColor: '#FFF',
height: '36px',
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
marginLeft: 32,
borderRadius: '0 0 6px 6px',
color: '#0077FA'
};
let innerSlotNode = (
点击加载更多
);
let outSlotStyle = {
backgroundColor: 'whitesmoke',
height: '36px',
display: 'flex',
paddingLeft: 32,
color: '#0077FA',
alignItems: 'center',
cursor: 'pointer',
borderRadius: '0 0 6px 6px',
};
let outSlotNode = (
未找到应用?
);
return (
outerBottomSlot:
innerBottomSlot:
);
};
```
通过 outerTopSlot 将内容插入顶部插槽
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
key: 'component',
};
this.list = {
component: [
{ value: 'select', label: '选择器' },
{ value: 'tabs', label: '标签' },
{ value: 'avatar', label: '头像' },
{ value: 'button', label: '按钮' },
],
design: [
{ value: 'color', label: '颜色' },
{ value: 'dark', label: '暗色模式' },
{ value: 'icon', label: '图标' },
{ value: 'font', label: '字体' },
],
feedback: [
{ value: 'faq', label: '常见问题' },
{ value: 'join', label: '加入用户群' },
{ value: 'hornbill', label: '犀鸟反馈问题' },
],
};
this.handleTabClick = this.handleTabClick.bind(this);
};
handleTabClick(itemKey) {
this.setState({key: itemKey});
};
render() {
const { key } = this.state;
const tabStyle = {
cursor: 'pointer',
marginRight: 12,
paddingBottom: 4,
};
const tabActiveStyle = {
...tabStyle,
borderBottom: '1px solid var(--semi-color-primary)',
fontWeight: 700,
};
const tabWrapper = {
display: 'flex',
paddingTop: 8,
paddingLeft: 32,
borderBottom: '0.5px solid var(--semi-color-border)'
};
const tabOptions = [
{itemKey: 'component', label: '组件'},
{itemKey: 'design', label: '设计'},
{itemKey: 'feedback', label: '反馈'},
];
const outerTopSlotNode = (
{
tabOptions.map((item, index) => {
style = item.itemKey === key ? tabActiveStyle : tabStyle;
return (
this.handleTabClick(item.itemKey)}>{item.label}
);
})
}
);
return (
);
};
}
```
### 受控组件
传入 value 时 Select 为受控组件,所选中的值完全由 value 决定。
```jsx live=true hideInDSM
import React, { useState } from 'react';
import { Select } from '@douyinfe/semi-ui';
() => {
let [value, setValue] = useState('xigua');
return (
<>
>
);
};
```
### 动态修改 Options
如果需要动态更新 Options,应该使用受控的 value
```jsx live=true hideInDSM
import React, { useState } from 'react';
import { Select, Button } from '@douyinfe/semi-ui';
() => {
let [options, setOptions] = useState([1, 2, 3, 4]);
function add() {
let length = Math.ceil(Math.random() * 10);
let newOptions = Array.from({length}, (v,i) => i+1);
setOptions(newOptions);
}
return (
<>
>
);
};
```
### 联动
使用受控value,实现不同Select之间的联动。如果是带有层级关系的复杂联动建议直接使用`Cascader`组件
```jsx live=true hideInDSM
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
class Link extends React.Component {
get provinces() {
return ['四川', '广东'];
}
get maps() {
return {
'四川': ['成都', '都江堰'],
'广东': ['广州', '深圳', '东莞'],
};
}
constructor() {
super();
this.state = {
provinces: this.provinces,
maps: this.maps,
citys: this.maps[this.provinces[0]],
city: this.maps[this.provinces[0]][0]
};
this.provinceChange = this.provinceChange.bind(this);
this.cityChange = this.cityChange.bind(this);
}
provinceChange(newProvince) {
const { maps } = this.state;
this.setState({ citys: maps[newProvince], city: maps[newProvince][0] });
}
cityChange(city) {
this.setState({ city });
}
render() {
const { provinces, citys, city } = this.state;
return (
);
}
}
```
### 开启搜索
将 `filter` 置为 true,开启搜索能力。默认搜索策略将为 input 输入值与 option 的 label 值进行 include 对比
```jsx live=true hideInDSM
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => (
<>
>
);
```
### 远程搜索
带有远程搜索,防抖请求,加载状态的多选示例
通过`filter`开启搜索能力
将`remote`设置为 true 关闭对当前数据的筛选过滤(在 v0.24.0 后提供)
通过动态更新`optionList`更新下拉菜单中的备选项
使用受控的 value 属性
```jsx live=true hideInDSM
import React from 'react';
import { debounce } from 'lodash-es';
import { Select } from '@douyinfe/semi-ui';
class SearchDemo extends React.Component {
constructor(){
super();
this.state = {
loading: false,
optionList: [
{ value: 'abc', label: '抖音', type: 1 },
{ value: 'hotsoon', label: '火山小视频', type: 2 },
{ value: 'jianying', label: '剪映', type: 3 },
{ value: 'toutiao', label: '今日头条', type: 4 },
],
value: '',
multipleValue: [],
};
this.handleSearch = debounce(this.handleSearch, 800).bind(this);
this.onChange = this.onChange.bind(this);
this.onMultipleChange = this.onMultipleChange.bind(this);
}
handleSearch(inputValue) {
this.setState({ loading: true });
let length = Math.ceil(Math.random()*100);
let result = Array.from({ length }, (v, i) => {
return { value: inputValue + i, label: inputValue + '-新业务线-' + i, type: i + 1 };
});
setTimeout(() => {
this.setState({ optionList: result, loading: false });
}, 2000);
}
onChange(value) {
this.setState({ value });
}
onMultipleChange(multipleValue) {
this.setState({ multipleValue });
}
render() {
const { loading, optionList, value, multipleValue } = this.state;
return (
);
}
}
```
### 自定义搜索逻辑
可以将 `filter` 置为自定义函数,定制你想要的搜索策略
如下例子,选项 label 值都是大写,默认的检索策略是字符串 include 对比,会区分大小写。
通过传入自定义 `filter` 函数,检索时输入小写字母也能搜到相应内容。
```jsx live=true hideInDSM
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => {
function searchLabel(sugInput, option) {
let label = option.label.toUpperCase();
let sug = sugInput.toUpperCase();
return label.includes(sug);
}
return (
);
};
```
### 自定义已选项标签渲染
默认情况下,选中选项后会将 option.label 或 option.children 的内容回填到选择框中
但你可以通过 `renderSelectedItem` 自定义选择框中已选项标签的渲染结构
- 单选时 `renderSelectedItem(optionNode:object) => content:ReactNode`
- 多选时 `renderSelectedItem(optionNode:object, { index:number, onClose:function }) => { isRenderInTag:bool, content:ReactNode }`
- isRenderInTag 为 true 时,会自动将 content 包裹在 Tag 中渲染(带有背景色以及关闭按钮)
- isRenderInTag 为 false 时,将直接渲染返回的 content
```jsx live=true
import React from 'react';
import { Select, Avatar, Tag } from '@douyinfe/semi-ui';
class CustomRender extends React.Component {
constructor() {
super();
this.state = {
list: [
{ "name": "夏可漫", "email": "xiakeman@example.com", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/avatarDemo.jpeg"},
{ "name": "申悦", "email": "shenyue@example.com", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg"},
{ "name": "曲晨一", "email": "quchenyi@example.com", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/8bd8224511db085ed74fea37205aede5.jpg"},
{ "name": "文嘉茂", "email": "wenjiamao@example.com", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/6fbafc2d-e3e6-4cff-a1e2-17709c680624.png"},
]
};
}
renderSelectedItem(optionNode) {
return (
{optionNode.abbr}
{optionNode.email}
);
}
// avatarSrc & avatarShape are supported after 1.6.0
renderMultipleWithCustomTag(optionNode, { onClose }) {
let content = (
{optionNode.name}
);
return {
isRenderInTag: false,
content
};
}
renderMultipleWithCustomTag2(optionNode, { onClose }) {
let content = (
{optionNode.name}
);
return {
isRenderInTag: false,
content
};
}
renderCustomOption(item) {
let optionStyle = {
display: 'flex',
paddingLeft: 24,
paddingTop: 10,
paddingBottom: 10
};
return (
);
}
render() {
const { list } = this.state;
return (
);
}
}
```
### 自定义弹出层样式
你可以通过 dropdownClassName、dropdownStyle 控制弹出层的样式
例如当自定义弹出层的宽度时,可以通过 dropdownStyle 传入 width
```jsx live=true hideInDSM
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => (
);
```
### 获取选项的其他属性
默认情况下`onChange`只能拿到 value,如果需要拿选中节点的其他属性,可以使用`onChangeWithObject`属性
此时`onChange`函数的入参将会是 object,包含 option 的各种属性,例如 `onChange({ value, label, ...rest })`
当 onChangeWithObject 置为 true 时,`defaultValue`/`Value`也应为 object,且须带有`value`、`label` key
```jsx live=true hideInDSM
import React from 'react';
import { Select, TextArea } from '@douyinfe/semi-ui';
() => {
const list = [
{ value: 'abc', label: '抖音', type: 1 },
{ value: 'hotsoon', label: '火山', type: 2 },
{ value: 'jianying', label: '剪映', type: 3 },
{ value: 'toutiao', label: '今日头条', type: 4 },
];
const [cbValue, setCbValue] = useState();
const [multipleCbValue, setMultipleCbValue] = useState();
const onChange = (value) => {
setCbValue(value);
console.log(value);
};
const onMultipleChange = (value) => {
setMultipleCbValue(value);
console.log(value);
};
return (
);
};
```
### 创建条目
设置`allowCreate`,可以创建并选中选项中不存在的条目
允许通过 `renderCreateItem` 自定义创建标签时的内容显示(通过返回 ReactNode,注意你需要自定义样式),该函数默认值为 (input) => '创建' + input
可以配合`defaultActiveFirstOption`属性使用,自动选中第一项,当输入完内容直接回车时,可立即创建
当开启allowCreate后,不会再响应对Children或者optionList的更新
```jsx live=true
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
() => {
const optionList = [
{ value: 'abc', label: '抖音' },
{ value: 'hotsoon', label: '火山小视频' },
{ value: 'jianying', label: '剪映' },
{ value: 'toutiao', label: '今日头条' },
];
return (
<>
>
);
};
```
### 虚拟化
传入`virtualize`时开启列表虚拟化,用于大量 Option 节点的情况优化性能
virtualize 是一个包含下列值的对象:
- height: Option 列表高度值,默认 300
- width: Option 列表宽度值,默认 100%
- itemSize: 每行 Option 的高度,必传
```jsx live=true hideInDSM
import React from 'react';
import { Select } from '@douyinfe/semi-ui';
class VirtualizeDemo extends React.Component {
constructor(props) {
super(props);
let newOptions = Array.from({ length: 3000 }, (v, i) => ({ label: `option-${i}`, value: i }));
this.state = {
optionList: newOptions,
};
}
render() {
let { groups, optionList } = this.state;
let virtualize = {
height: 300,
width: '100%',
itemSize: 36, // px
};
return (
<>
>
);
}
}
```
### 自定义触发器
如果 Select 默认的触发器样式满足不了你的需求,可以用`triggerRender`自定义选择框的展示
triggerRender 入参如下
```typescript
interface triggerRenderProps {
value: array