Select选择器
下拉选择器。
何时使用
弹出一个下拉菜单给用户选择操作,用于代替原生的选择器,或者需要一个更优雅的多选器时。
当选项少时(少于 5 项),建议直接将选项平铺,使用 Radio 是更好的选择。
代码演示

基本使用。
import { Select } from 'antd';const { Option } = Select;function handleChange(value) {console.log(`selected ${value}`);}ReactDOM.render(<><Select defaultValue="lucy" style={{ width: 120 }} onChange={handleChange}><Option value="jack">Jack</Option><Option value="lucy">Lucy</Option><Option value="disabled" disabled>Disabled</Option><Option value="Yiminghe">yiminghe</Option></Select><Select defaultValue="lucy" style={{ width: 120 }} disabled><Option value="lucy">Lucy</Option></Select><Select defaultValue="lucy" style={{ width: 120 }} loading><Option value="lucy">Lucy</Option></Select><Select defaultValue="lucy" style={{ width: 120 }} allowClear><Option value="lucy">Lucy</Option></Select></>,mountNode,);

多选,从已有条目中选择。
import { Select } from 'antd';const { Option } = Select;const children = [];for (let i = 10; i < 36; i++) {children.push(<Option key={i.toString(36) + i}>{i.toString(36) + i}</Option>);}function handleChange(value) {console.log(`selected ${value}`);}ReactDOM.render(<><Selectmode="multiple"allowClearstyle={{ width: '100%' }}placeholder="Please select"defaultValue={['a10', 'c12']}onChange={handleChange}>{children}</Select><br /><Selectmode="multiple"disabledstyle={{ width: '100%' }}placeholder="Please select"defaultValue={['a10', 'c12']}onChange={handleChange}>{children}</Select></>,mountNode,);

使用 optionLabelProp 指定回填到选择框的 Option 属性。
import { Select } from 'antd';const { Option } = Select;function handleChange(value) {console.log(`selected ${value}`);}ReactDOM.render(<Selectmode="multiple"style={{ width: '100%' }}placeholder="select one country"defaultValue={['china']}onChange={handleChange}optionLabelProp="label"><Option value="china" label="China"><div className="demo-option-label-item"><span role="img" aria-label="China">🇨🇳</span>China (中国)</div></Option><Option value="usa" label="USA"><div className="demo-option-label-item"><span role="img" aria-label="USA">🇺🇸</span>USA (美国)</div></Option><Option value="japan" label="Japan"><div className="demo-option-label-item"><span role="img" aria-label="Japan">🇯🇵</span>Japan (日本)</div></Option><Option value="korea" label="Korea"><div className="demo-option-label-item"><span role="img" aria-label="Korea">🇰🇷</span>Korea (韩国)</div></Option></Select>,mountNode,);
.demo-option-label-item > span {margin-right: 6px;}

tags select,随意输入的内容(scroll the menu)。
import { Select } from 'antd';const { Option } = Select;const children = [];for (let i = 10; i < 36; i++) {children.push(<Option key={i.toString(36) + i}>{i.toString(36) + i}</Option>);}function handleChange(value) {console.log(`selected ${value}`);}ReactDOM.render(<Select mode="tags" style={{ width: '100%' }} placeholder="Tags Mode" onChange={handleChange}>{children}</Select>,mountNode,);

省市联动是典型的例子。
推荐使用 Cascader 组件。
import { Select } from 'antd';const { Option } = Select;const provinceData = ['Zhejiang', 'Jiangsu'];const cityData = {Zhejiang: ['Hangzhou', 'Ningbo', 'Wenzhou'],Jiangsu: ['Nanjing', 'Suzhou', 'Zhenjiang'],};const App = () => {const [cities, setCities] = React.useState(cityData[provinceData[0]]);const [secondCity, setSecondCity] = React.useState(cityData[provinceData[0]][0]);const handleProvinceChange = value => {setCities(cityData[value]);setSecondCity(cityData[value][0]);};const onSecondCityChange = value => {setSecondCity(value);};return (<><Select defaultValue={provinceData[0]} style={{ width: 120 }} onChange={handleProvinceChange}>{provinceData.map(province => (<Option key={province}>{province}</Option>))}</Select><Select style={{ width: 120 }} value={secondCity} onChange={onSecondCityChange}>{cities.map(city => (<Option key={city}>{city}</Option>))}</Select></>);};ReactDOM.render(<App />, mountNode);

默认情况下 onChange 里只能拿到 value,如果需要拿到选中的节点文本 label,可以使用 labelInValue 属性。
选中项的 label 会被包装到 value 中传递给 onChange 等函数,此时 value 是一个对象。
import { Select } from 'antd';const { Option } = Select;function handleChange(value) {console.log(value); // { value: "lucy", key: "lucy", label: "Lucy (101)" }}ReactDOM.render(<SelectlabelInValuedefaultValue={{ value: 'lucy' }}style={{ width: 120 }}onChange={handleChange}><Option value="jack">Jack (100)</Option><Option value="lucy">Lucy (101)</Option></Select>,mountNode,);

一个带有远程搜索,防抖控制,请求时序控制,加载状态的多选示例。
TypeScript
JavaScript

import { Select, Spin } from 'antd';import { SelectProps } from 'antd/es/select';import debounce from 'lodash/debounce';export interface DebounceSelectProps<ValueType = any>extends Omit<SelectProps<ValueType>, 'options' | 'children'> {fetchOptions: (search: string) => Promise<ValueType[]>;debounceTimeout?: number;}function DebounceSelect<ValueType extends { key?: string; label: React.ReactNode; value: string | number } = any>({ fetchOptions, debounceTimeout = 800, ...props }: DebounceSelectProps) {const [fetching, setFetching] = React.useState(false);const [options, setOptions] = React.useState<ValueType[]>([]);const fetchRef = React.useRef(0);const debounceFetcher = React.useMemo(() => {const loadOptions = (value: string) => {fetchRef.current += 1;const fetchId = fetchRef.current;setOptions([]);setFetching(true);fetchOptions(value).then(newOptions => {if (fetchId !== fetchRef.current) {// for fetch callback orderreturn;}setOptions(newOptions);setFetching(false);});};return debounce(loadOptions, debounceTimeout);}, [fetchOptions, debounceTimeout]);return (<Select<ValueType>labelInValuefilterOption={false}onSearch={debounceFetcher}notFoundContent={fetching ? <Spin size="small" /> : null}{...props}options={options}/>);}// Usage of DebounceSelectinterface UserValue {label: string;value: string;}async function fetchUserList(username: string): Promise<UserValue[]> {console.log('fetching user', username);return fetch('https://randomuser.me/api/?results=5').then(response => response.json()).then(body =>body.results.map((user: { name: { first: string; last: string }; login: { username: string } }) => ({label: `${user.name.first} ${user.name.last}`,value: user.login.username,}),),);}const Demo = () => {const [value, setValue] = React.useState<UserValue[]>([]);return (<DebounceSelectmode="multiple"value={value}placeholder="Select users"fetchOptions={fetchUserList}onChange={newValue => {setValue(newValue);}}style={{ width: '100%' }}/>);};ReactDOM.render(<Demo />, mountNode);

隐藏下拉列表中已选择的选项。
import { Select } from 'antd';const OPTIONS = ['Apples', 'Nails', 'Bananas', 'Helicopters'];class SelectWithHiddenSelectedOptions extends React.Component {state = {selectedItems: [],};handleChange = selectedItems => {this.setState({ selectedItems });};render() {const { selectedItems } = this.state;const filteredOptions = OPTIONS.filter(o => !selectedItems.includes(o));return (<Selectmode="multiple"placeholder="Inserted are removed"value={selectedItems}onChange={this.handleChange}style={{ width: '100%' }}>{filteredOptions.map(item => (<Select.Option key={item} value={item}>{item}</Select.Option>))}</Select>);}}ReactDOM.render(<SelectWithHiddenSelectedOptions />, mountNode);

允许自定义选择标签的样式。
import { Select, Tag } from 'antd';const options = [{ value: 'gold' }, { value: 'lime' }, { value: 'green' }, { value: 'cyan' }];function tagRender(props) {const { label, value, closable, onClose } = props;const onPreventMouseDown = event => {event.preventDefault();event.stopPropagation();};return (<Tagcolor={value}onMouseDown={onPreventMouseDown}closable={closable}onClose={onClose}style={{ marginRight: 3 }}>{label}</Tag>);}ReactDOM.render(<Selectmode="multiple"showArrowtagRender={tagRender}defaultValue={['gold', 'cyan']}style={{ width: '100%' }}options={options}/>,mountNode,);

Select 使用了虚拟滚动技术,因而获得了比 3.0 更好的性能。
import { Select, Typography, Divider } from 'antd';const { Title } = Typography;const options = [];for (let i = 0; i < 100000; i++) {const value = `${i.toString(36)}${i}`;options.push({value,disabled: i === 10,});}function handleChange(value) {console.log(`selected ${value}`);}ReactDOM.render(<><Title level={3}>Ant Design 4.0</Title><Title level={4}>{options.length} Items</Title><Selectmode="multiple"style={{ width: '100%' }}placeholder="Please select"defaultValue={['a10', 'c12']}onChange={handleChange}options={options}/><Divider /><Title level={3}>Ant Design 3.0</Title><iframetitle="Ant Design 3.0 Select demo"src="https://codesandbox.io/embed/solitary-voice-m3vme?fontsize=14&hidenavigation=1&theme=dark&view=preview"style={{ width: '100%', height: 300 }}/></>,mountNode,);

展开后可对选项进行搜索。
import { Select } from 'antd';const { Option } = Select;function onChange(value) {console.log(`selected ${value}`);}function onBlur() {console.log('blur');}function onFocus() {console.log('focus');}function onSearch(val) {console.log('search:', val);}ReactDOM.render(<SelectshowSearchstyle={{ width: 200 }}placeholder="Select a person"optionFilterProp="children"onChange={onChange}onFocus={onFocus}onBlur={onBlur}onSearch={onSearch}filterOption={(input, option) =>option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}><Option value="jack">Jack</Option><Option value="lucy">Lucy</Option><Option value="tom">Tom</Option></Select>,mountNode,);

三种大小的选择框,当 size 分别为 large 和 small 时,输入框高度为 40px 和 24px ,默认高度为 32px。
import { Select, Radio } from 'antd';const { Option } = Select;const children = [];for (let i = 10; i < 36; i++) {children.push(<Option key={i.toString(36) + i}>{i.toString(36) + i}</Option>);}function handleChange(value) {console.log(`Selected: ${value}`);}const SelectSizesDemo = () => {const [size, setSize] = React.useState('default');const handleSizeChange = e => {setSize(e.target.value);};return (<><Radio.Group value={size} onChange={handleSizeChange}><Radio.Button value="large">Large</Radio.Button><Radio.Button value="default">Default</Radio.Button><Radio.Button value="small">Small</Radio.Button></Radio.Group><br /><br /><Select size={size} defaultValue="a1" onChange={handleChange} style={{ width: 200 }}>{children}</Select><br /><Selectmode="multiple"size={size}placeholder="Please select"defaultValue={['a10', 'c12']}onChange={handleChange}style={{ width: '100%' }}>{children}</Select><br /><Selectmode="tags"size={size}placeholder="Please select"defaultValue={['a10', 'c12']}onChange={handleChange}style={{ width: '100%' }}>{children}</Select></>);};ReactDOM.render(<SelectSizesDemo />, mountNode);
.code-box-demo .ant-select {margin: 0 8px 10px 0;}.ant-row-rtl .code-box-demo .ant-select {margin: 0 0 10px 8px;}#components-select-demo-search-box .code-box-demo .ant-select {margin: 0;}

在搜索模式下对过滤结果项进行排序。
import { Select } from 'antd';const { Option } = Select;ReactDOM.render(<SelectshowSearchstyle={{ width: 200 }}placeholder="Search to Select"optionFilterProp="children"filterOption={(input, option) =>option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}filterSort={(optionA, optionB) =>optionA.children.toLowerCase().localeCompare(optionB.children.toLowerCase())}><Option value="1">Not Identified</Option><Option value="2">Closed</Option><Option value="3">Communicated</Option><Option value="4">Identified</Option><Option value="5">Resolved</Option><Option value="6">Cancelled</Option></Select>,mountNode,);

用 OptGroup 进行选项分组。
import { Select } from 'antd';const { Option, OptGroup } = Select;function handleChange(value) {console.log(`selected ${value}`);}ReactDOM.render(<Select defaultValue="lucy" style={{ width: 200 }} onChange={handleChange}><OptGroup label="Manager"><Option value="jack">Jack</Option><Option value="lucy">Lucy</Option></OptGroup><OptGroup label="Engineer"><Option value="Yiminghe">yiminghe</Option></OptGroup></Select>,mountNode,);

搜索和远程数据结合。
import { Select } from 'antd';import jsonp from 'fetch-jsonp';import querystring from 'querystring';const { Option } = Select;let timeout;let currentValue;function fetch(value, callback) {if (timeout) {clearTimeout(timeout);timeout = null;}currentValue = value;function fake() {const str = querystring.encode({code: 'utf-8',q: value,});jsonp(`https://suggest.taobao.com/sug?${str}`).then(response => response.json()).then(d => {if (currentValue === value) {const { result } = d;const data = [];result.forEach(r => {data.push({value: r[0],text: r[0],});});callback(data);}});}timeout = setTimeout(fake, 300);}class SearchInput extends React.Component {state = {data: [],value: undefined,};handleSearch = value => {if (value) {fetch(value, data => this.setState({ data }));} else {this.setState({ data: [] });}};handleChange = value => {this.setState({ value });};render() {const options = this.state.data.map(d => <Option key={d.value}>{d.text}</Option>);return (<SelectshowSearchvalue={this.state.value}placeholder={this.props.placeholder}style={this.props.style}defaultActiveFirstOption={false}showArrow={false}filterOption={false}onSearch={this.handleSearch}onChange={this.handleChange}notFoundContent={null}>{options}</Select>);}}ReactDOM.render(<SearchInput placeholder="input search text" style={{ width: 200 }} />, mountNode);

试下复制 露西,杰克 并粘贴到输入框里。只在 tags 和 multiple 模式下可用。
import { Select } from 'antd';const { Option } = Select;const children = [];for (let i = 10; i < 36; i++) {children.push(<Option key={i.toString(36) + i}>{i.toString(36) + i}</Option>);}function handleChange(value) {console.log(`selected ${value}`);}ReactDOM.render(<Select mode="tags" style={{ width: '100%' }} onChange={handleChange} tokenSeparators={[',']}>{children}</Select>,mountNode,);

使用 dropdownRender 对下拉菜单进行自由扩展。
import { Select, Divider, Input } from 'antd';import { PlusOutlined } from '@ant-design/icons';const { Option } = Select;let index = 0;class App extends React.Component {state = {items: ['jack', 'lucy'],name: '',};onNameChange = event => {this.setState({name: event.target.value,});};addItem = () => {console.log('addItem');const { items, name } = this.state;this.setState({items: [...items, name || `New item ${index++}`],name: '',});};render() {const { items, name } = this.state;return (<Selectstyle={{ width: 240 }}placeholder="custom dropdown render"dropdownRender={menu => (<div>{menu}<Divider style={{ margin: '4px 0' }} /><div style={{ display: 'flex', flexWrap: 'nowrap', padding: 8 }}><Input style={{ flex: 'auto' }} value={name} onChange={this.onNameChange} /><astyle={{ flex: 'none', padding: '8px', display: 'block', cursor: 'pointer' }}onClick={this.addItem}><PlusOutlined /> Add item</a></div></div>)}>{items.map(item => (<Option key={item}>{item}</Option>))}</Select>);}}ReactDOM.render(<App />, mountNode);

无边框样式。
import { Select } from 'antd';const { Option } = Select;ReactDOM.render(<><Select defaultValue="lucy" style={{ width: 120 }} bordered={false}><Option value="jack">Jack</Option><Option value="lucy">Lucy</Option><Option value="Yiminghe">yiminghe</Option></Select><Select defaultValue="lucy" style={{ width: 120 }} disabled bordered={false}><Option value="lucy">Lucy</Option></Select></>,mountNode,);

多选下通过响应式布局让选项自动收缩。该功能对性能有所消耗,不推荐在大表单场景下使用。
TypeScript
JavaScript

import { Select, Space } from 'antd';interface ItemProps {label: string;value: string;}const options: ItemProps[] = [];for (let i = 10; i < 36; i++) {const value = i.toString(36) + i;options.push({label: `Long Label: ${value}`,value,});}const Demo = () => {const [value, setValue] = React.useState(['a10', 'c12', 'h17', 'j19', 'k20']);const selectProps = {mode: 'multiple' as const,style: { width: '100%' },value,options,onChange: (newValue: string[]) => {setValue(newValue);},placeholder: 'Select Item...',maxTagCount: 'responsive' as const,};return (<Space direction="vertical" style={{ width: '100%' }}><Select {...selectProps} /><Select {...selectProps} disabled /></Space>);};ReactDOM.render(<Demo />, mountNode);
API
<Select><Option value="lucy">lucy</Option></Select>
Select props
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| allowClear | 支持清除 | boolean | false | |
| autoClearSearchValue | 是否在选中项后清空搜索框,只在 mode 为 multiple 或 tags 时有效 | boolean | true | |
| autoFocus | 默认获取焦点 | boolean | false | |
| bordered | 是否有边框 | boolean | true | |
| clearIcon | 自定义的多选框清空图标 | ReactNode | - | |
| defaultActiveFirstOption | 是否默认高亮第一个选项 | boolean | true | |
| defaultOpen | 是否默认展开下拉菜单 | boolean | - | |
| defaultValue | 指定默认选中的条目 | string | string[] number | number[] LabeledValue | LabeledValue[] | - | |
| disabled | 是否禁用 | boolean | false | |
| dropdownClassName | 下拉菜单的 className 属性 | string | - | |
| dropdownMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 min-width,当值小于选择框宽度时会被忽略。false 时会关闭虚拟滚动 | boolean | number | true | |
| dropdownRender | 自定义下拉框内容 | (originNode: ReactNode) => ReactNode | - | |
| dropdownStyle | 下拉菜单的 style 属性 | CSSProperties | - | |
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 inputValue option 两个参数,当 option 符合筛选条件时,应返回 true,反之则返回 false | boolean | function(inputValue, option) | true | |
| filterSort | 搜索时对筛选结果项的排序函数, 类似Array.sort里的 compareFunction | (optionA: Option, optionB: Option) => number | - | 4.9.0 |
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。示例 | function(triggerNode) | () => document.body | |
| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 string 变为 { value: string, label: ReactNode } 的格式 | boolean | false | |
| listHeight | 设置弹窗滚动高度 | number | 256 | |
| loading | 加载中状态 | boolean | false | |
| maxTagCount | 最多显示多少个 tag,响应式模式会对性能产生损耗 | number | responsive | - | responsive: 4.10 |
| maxTagPlaceholder | 隐藏 tag 时显示的内容 | ReactNode | function(omittedValues) | - | |
| maxTagTextLength | 最大显示的 tag 文本长度 | number | - | |
| menuItemSelectedIcon | 自定义多选时当前选中的条目图标 | ReactNode | - | |
| mode | 设置 Select 的模式为多选或标签 | multiple | tags | - | |
| notFoundContent | 当下拉列表为空时显示的内容 | ReactNode | Not Found | |
| open | 是否展开下拉菜单 | boolean | - | |
| optionFilterProp | 搜索时过滤对应的 option 属性,如设置为 children 表示对内嵌内容进行搜索。若通过 options 属性配置选项内容,建议设置 optionFilterProp=”label” 来对内容进行搜索。 | string | value | |
| optionLabelProp | 回填到选择框的 Option 的属性值,默认是 Option 的子元素。比如在子元素需要高亮效果时,此值可以设为 value。示例 | string | children | |
| options | 数据化配置选项内容,相比 jsx 定义会获得更好的渲染性能 | { label, value }[] | - | |
| placeholder | 选择框默认文本 | string | - | |
| removeIcon | 自定义的多选框清除图标 | ReactNode | - | |
| searchValue | 控制搜索文本 | string | - | |
| showArrow | 是否显示下拉小箭头 | boolean | 单选为 true,多选为 false | |
| showSearch | 使单选模式可搜索 | boolean | false | |
| size | 选择框大小 | large | middle | small | middle | |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
| tagRender | 自定义 tag 内容 render | (props) => ReactNode | - | |
| tokenSeparators | 在 tags 和 multiple 模式下自动分词的分隔符 | string[] | - | |
| value | 指定当前选中的条目 | string | string[] number | number[] LabeledValue | LabeledValue[] | - | |
| virtual | 设置 false 时关闭虚拟滚动 | boolean | true | 4.1.0 |
| onBlur | 失去焦点时回调 | function | - | |
| onChange | 选中 option,或 input 的 value 变化时,调用此函数 | function(value, option:Option | Array<Option>) | - | |
| onClear | 清除内容时回调 | function | - | 4.6.0 |
| onDeselect | 取消选中时调用,参数为选中项的 value (或 key) 值,仅在 multiple 或 tags 模式下生效 | function(string | number | LabeledValue) | - | |
| onDropdownVisibleChange | 展开下拉菜单的回调 | function(open) | - | |
| onFocus | 获得焦点时回调 | function | - | |
| onInputKeyDown | 按键按下时回调 | function | - | |
| onMouseEnter | 鼠标移入时回调 | function | - | |
| onMouseLeave | 鼠标移出时回调 | function | - | |
| onPopupScroll | 下拉列表滚动时的回调 | function | - | |
| onSearch | 文本框值变化时回调 | function(value: string) | - | |
| onSelect | 被选中时调用,参数为选中项的 value (或 key) 值 | function(string | number | LabeledValue, option: Option) | - |
注意,如果发现下拉菜单跟随页面滚动,或者需要在其他弹层中触发 Select,请尝试使用
getPopupContainer={triggerNode => triggerNode.parentElement}将下拉弹层渲染节点固定在触发器的父元素中。
Select Methods
| 名称 | 说明 | 版本 |
|---|---|---|
| blur() | 取消焦点 | |
| focus() | 获取焦点 |
Option props
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| className | Option 器类名 | string | - | |
| disabled | 是否禁用 | boolean | false | |
| title | 选中该 Option 后,Select 的 title | string | - | |
| value | 默认根据此属性值进行筛选 | string | number | - |
OptGroup props
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| key | Key | string | - | |
| label | 组名 | string | React.Element | - |
FAQ
tag 模式下为何搜索有时会出现两个相同选项?
这一般是 options 中的 label 和 value 不同导致的,你可以通过 optionFilterProp="label" 将过滤设置为展示值以避免这种情况。
点击 dropdownRender 里的内容浮层关闭怎么办?
看下 dropdownRender 例子 里的说明。
自定义 Option 样式导致滚动异常怎么办?
这是由于虚拟滚动默认选项高度为 32px,如果你的选项高度小于该值则需要通过 listItemHeight 属性调整,而 listHeight 用于设置滚动容器高度:
<Select listItemHeight={10} listHeight={250} />
注意:listItemHeight 和 listHeight 为内部属性,如无必要,请勿修改该值。
为何无障碍测试会报缺失 aria- 属性?
Select 无障碍辅助元素仅在弹窗展开时创建,因而当你在进行无障碍检测时请先打开下拉后再进行测试。对于 aria-label 与 aria-labelledby 属性缺失警告,请自行为 Select 组件添加相应无障碍属性。