--- localeCode: zh-CN order: 27 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'; () => ( <>

); ``` ### 内嵌标签 通过设置`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 innerSlotStyle = { backgroundColor: 'var(--color-white)', height: '36px', display: 'flex', alignItems: 'center', cursor: 'pointer', paddingLeft: 32, borderTop: '1px solid var(--semi-color-border)', borderRadius: '0 0 6px 6px', color: 'var(--semi-color-link)', }; let innerSlotNode = (
点击加载更多
); let outSlotStyle = { backgroundColor: 'var(--semi-color-fill-0)', height: '36px', display: 'flex', paddingLeft: 32, color: 'var(--semi-color-link)', alignItems: 'center', cursor: 'pointer', borderTop: '1px solid var(--semi-color-border)', borderRadius: '0 0 6px 6px', }; let outSlotNode = (
未找到应用?
); return (

outerBottomSlot:

innerBottomSlot:

); }; ``` 通过 outerTopSlot 将内容插入顶部插槽 ```jsx live=true import React from 'react'; import { Select } from '@douyinfe/semi-ui'; () => { const 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: '犀鸟反馈问题' }, ], }; const [key, setKey] = useState('component'); const [value, setValue] = useState({ value: 'faq', label: '常见问题' },); const handleTabClick = (itemKey) => { setKey(itemKey); }; 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 (
handleTabClick(item.itemKey)}>{item.label}
); }) }
); 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 对比 默认情况下,多选选中后会自动清空搜索关键字。若你希望保留,可以通过 autoClearSearchValue 设为 false 关闭默认行为(v2.3后提供) ```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'; () => { const list = [ { "name": "夏可漫", "email": "xiakeman@example.com", "avatar": "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dy.png" }, { "name": "申悦", "email": "shenyue@example.com", "avatar": "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/bag.jpeg" }, { "name": "曲晨一", "email": "quchenyi@example.com", "avatar": "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/Viamaker.png" }, { "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" }, ]; const renderSelectedItem = optionNode => (
{optionNode.abbr} {optionNode.email}
); // avatarSrc & avatarShape are supported after 1.6.0-beta const renderMultipleWithCustomTag = (optionNode, { onClose }) => { const content = ( {optionNode.name} ); return { isRenderInTag: false, content }; }; const renderMultipleWithCustomTag2 = (optionNode, { onClose }) => { const content = ( {optionNode.name} ); return { isRenderInTag: false, content }; }; const renderCustomOption = (item, index) => { const optionStyle = { display: 'flex', paddingLeft: 24, paddingTop: 10, paddingBottom: 10 }; return (
{item.name}
{item.email}
); }; 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 (

onChange回调: