Ant Design Dropdown 可选中菜单实战:通过menu.selectable开启下拉项选择能力
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design
Dropdown(下拉菜单)在 Ant Design 中常用于"多个选项择一执行"的场景,而官方文档给出的这一则示例正是它的一个重要进阶用法:让下拉菜单里的选项具备"选中"能力。本指南以 components/dropdown/demo/selectable.md 及其配套示例为核心,讲解如何开启选择态、相关 MenuProps 字段的配合方式,并结合 dropdown 源码 揭示选中后下拉面板自动收起与否的真实逻辑。读完你可以直接在自己的 Dropdown 中实现单选、多选与受控选中三种形态。
示例要解决的问题:让下拉菜单项"可以被选中"
官方 Demo 的说明非常简洁,原文如下:
zh-CN:添加
menu中的selectable属性可以开启选择能力。 en-US:Configure theselectableproperty inmenuto enable selectable ability.
翻译成实际效果就是:Dropdown 默认只负责"弹出菜单、点击执行动作",菜单项并不会停留在"被选中"的高亮/勾选状态;只要你在 Dropdown 的menu配置对象里显式打开selectable: true,菜单项就会进入可选择模式,被点击的项会呈现选中态。这与导航型Menu中管理当前激活项是同一套状态体系,只是入口被搬到了 Dropdown 的menu属性上。
示例的可运行代码位于 components/dropdown/demo/selectable.tsx,同时这段示例也以 "Selectable Menu" 的标题挂在 Dropdown 官方 API 文档 的示例列表中(见该文档第 34 行)。
完整示例拆解:逐字段看懂最小可用写法
下面把示例代码完整还原并加上注释:
import React from 'react'; import { DownOutlined } from '@ant-design/icons'; import type { MenuProps } from 'antd'; import { Dropdown, Space, Typography } from 'antd'; const items: MenuProps['items'] = [ { key: '1', label: 'Item 1' }, { key: '2', label: 'Item 2' }, { key: '3', label: 'Item 3' }, ]; const App: React.FC = () => ( <Dropdown menu={{ items, selectable: true, // 关键开关:开启菜单项选择能力 defaultSelectedKeys: ['3'], // 初始即让 key 为 '3' 的菜单项处于选中态 }} > {/* 触发元素:整行都是可点击的下拉触发器 */} <Typography.Link> <Space> Selectable <DownOutlined /> {/* 经典的“下箭头”下拉指示图标 */} </Space> </Typography.Link> </Dropdown> ); export default App;这个例子只动用了三个要点,就可以直接跑通"可选中下拉":
menu.items:以对象数组(MenuProps['items'])声明菜单项。每个菜单项必须有唯一的key,因为选择状态(selected keys)正是以 key 为维度进行维护的;menu.selectable:置为true,菜单项可被选中,被点中的项会出现选中态样式;menu.defaultSelectedKeys:非受控场景下声明"哪些 key 默认处于选中态"。这里传['3'],所以面板第一次打开时第 3 项就已经是勾选状态。
需要说明的是,Dropdown自身并没有一个叫selectable的属性——选择能力完全来自其menu属性。从 dropdown.tsx 的类型定义可以看到,menu的类型就是完整的 MenuProps:
// components/dropdown/dropdown.tsx menu?: MenuProps & { activeKey?: RcMenuProps['activeKey'] };同时该文件顶部import type { MenuProps } from '../menu'(见 dropdown.tsx),说明 Dropdown 的下拉菜单底层复用的就是 Menu 组件,凡是 Menu 支持的选择类能力,Dropdown 的menu配置里都能用。
与选择能力相关的 MenuProps 字段全家桶
要发挥selectable的完整价值,通常需要和下面几个 MenuProps 字段配合。它们既适用于独立 Menu,也适用于 Dropdown 的menu配置:
| 字段 | 类型 | 作用 |
|---|---|---|
selectable | boolean | 是否开启菜单项选择能力,true时被点击项显示选中态 |
multiple | boolean | 是否允许多选(需与selectable同开才有意义) |
defaultSelectedKeys | string[] | 非受控:设置初始选中项集合 |
selectedKeys | string[] | 受控:由外部状态决定当前选中项集合 |
onSelect | ({ key, ... }) => void | 用户新选中某项时触发的回调 |
onDeselect | ({ key, ... }) => void | 用户取消选中某项时触发(多选场景) |
配合思路可以总结为两条使用路径:
- 非受控(最简单):只声明
selectable+defaultSelectedKeys,让 Menu 自己维护内部选中状态,例如示例中defaultSelectedKeys: ['3']的用法。适合只需要“开启选中交互、不必把选择结果同步给业务代码”的场景。 - 受控(需要回读选中值):同时声明
selectable+selectedKeys,并在onSelect/onDeselect中更新外部 state。此时选中的真正“事实来源”在业务侧,适合需要把当前选中项上报、存储或影响其他 UI 的场景。
仓库中另一个配套示例 components/dropdown/demo/selection.tsx(在 API 文档 中以 "Selection actions" 命名)正是在此基础上的延伸演示,展示了选中动作与业务逻辑联动时的典型写法,建议对照阅读。
源码视角:下拉面板里的 Menu 是如何被组装出来的
要理解selectable为什么放在menu里就能生效,可以看 dropdown.tsx 中renderOverlay的实现:当用户提供了menu.items时,Dropdown 会把整个menu对象原样展开渲染成一个真正的Menu组件:
// components/dropdown/dropdown.tsx(renderOverlay 内部,节选) if (menu?.items) { overlayNode = ( <Menu {...menu} ... /> ); }也就是说,selectable、defaultSelectedKeys这些字段最终是被透传给了内部的标准 Menu。同时,这段 Menu 又被一个OverrideProvider包裹,其中强制约定了几条下拉菜单的默认行为(见 dropdown.tsx):
<OverrideProvider prefixCls={`${prefixCls}-menu`} ... mode="vertical" // 下拉菜单固定为垂直排布 selectable={false} // 默认关闭可选中,需要用户主动在 menu.selectable 中打开 onClick={onMenuClick} ... >这一小段代码正好印证了官方示例文案里的两句话:
- 为什么需要“添加
selectable属性”才能开启?因为 OverrideProvider 给下拉内部菜单的默认值就是selectable: false,示例显式传入selectable: true即是对默认值的覆盖,符合直觉; mode="vertical"说明下拉菜单内部始终以垂直模式渲染,因此你不需要、也不应该在 Dropdown 的菜单上自行指定其他mode。
一个易被忽略的行为:选中后面板会不会自动收起?
选择能力与“点击菜单项后面板关闭”的默认交互会发生叠加,处理逻辑在 dropdown.tsx 的onMenuClick中:
const onMenuClick = useEvent(() => { if (menu?.selectable && menu?.multiple) { return; // 可选中且为多选:点完不收起,方便连续勾选 } onOpenChange?.(false, { source: 'menu' }); setOpen(false); // 否则:点击菜单项后自动收起 });由此可以归纳出官方刻意设计的两套行为,这也是实测中最容易踩坑的点:
- 单选场景(
selectable: true,未开multiple):用户点击某个菜单项后,下拉面板会照常自动收起,配合触发元素上的onOpenChange与onSelect即可完成“选一个、关面板、拿到值”的完整闭环,这也是最常见的用法; - 多选场景(
selectable: true+multiple: true):面板不会自动收起,用户可以连续点选/取消多个选项,选完后再点击面板外部将其关闭。如果此时你期待“每点一项就自动收起”,将会发现行为与预期不同——这正是上面这段源码想表达的判断条件。
从单选到多选:一个可直接落地的进阶示例
基于同一套字段,开启多选只需要额外加上multiple: true,并配合受控的selectedKeys读取结果:
import React, { useState } from 'react'; import { DownOutlined } from '@ant-design/icons'; import type { MenuProps } from 'antd'; import { Dropdown, Space, Typography } from 'antd'; const items: MenuProps['items'] = [ { key: 'a', label: 'React' }, { key: 'b', label: 'Vue' }, { key: 'c', label: 'Svelte' }, ]; const App: React.FC = () => { const [selectedKeys, setSelectedKeys] = useState<string[]>(['a']); const menuProps: MenuProps = { items, selectable: true, multiple: true, selectedKeys, onSelect: ({ key }) => setSelectedKeys((prev) => [...prev, key]), onDeselect: ({ key }) => setSelectedKeys((prev) => prev.filter((k) => k !== key)), }; return ( <Dropdown menu={menuProps} trigger={['click']}> <Typography.Link> <Space> 已选 {selectedKeys.length} 项:{selectedKeys.join(', ')} </Space> <DownOutlined /> </Typography.Link> </Dropdown> ); }; export default App;注意此处由于是multiple模式,依据上文源码逻辑,点击项后面板不会收起,用户可以通过点击空白区域关闭。若你希望选中某个值后立即把结果汇报出去并关闭面板,则应采用单选(去掉multiple)并用onSelect同步状态。
使用要点与避坑清单
key必须唯一且稳定:选择状态、展开状态都以 key 索引,重复 key 会导致勾选与事件错乱;- 先开
selectable,再谈defaultSelectedKeys/selectedKeys:不开selectable时,即便传入选中 key,菜单项也不会进入可选择/勾选的可视状态; - 单选默认自动收起,多选默认不收起:这是 dropdown.tsx 中
selectable && multiple判断直接决定的,不要依赖直觉猜测,需要保持展开请走多选分支; - 触发元素需要支持事件冒泡:Dropdown 依赖子节点响应
onMouseEnter、onMouseLeave、onFocus、onClick(见 API 文档 的 Note 一节),使用Typography.Link等标准元素即可,自定义组件需确保这些事件能正常触发; - 区分非受控与受控:演示类代码常用
defaultSelectedKeys省事,业务中需要回读选中值时,请切换为selectedKeys+onSelect/onDeselect的受控写法。
相关阅读
- 示例描述文档:components/dropdown/demo/selectable.md
- 示例实现代码:components/dropdown/demo/selectable.tsx
- 选择动作进阶示例:components/dropdown/demo/selection.tsx
- Dropdown 核心实现(
renderOverlay、OverrideProvider、onMenuClick):components/dropdown/dropdown.tsx - Menu 上下文覆盖逻辑(
selectable默认值来源):components/menu/OverrideContext.tsx - Dropdown 完整 API 与全部示例索引:components/dropdown/index.en-US.md
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考