本文详解如何在 Ant Design 的 Select[mode="multiple"] 中动态显示用户选择顺序(如 Label-1、Label-2),并在标签(Tag)和下拉选项中同步更新序号,同时避免高频重渲染性能问题。
本文详解如何在 ant design 的 `select[mode="multiple"]` 中动态显示用户选择顺序(如 label-1、label-2),并在标签(tag)和下拉选项中同步更新序号,同时避免高频重渲染性能问题。
在 Ant Design 的多选组件中,默认仅维护选中值的集合(string[] 或 number[]),不保留选择时序信息。但实际业务中常需向用户明确传达“哪个先选、哪个后选”——例如配置字段优先级、构建有序筛选条件、或实现拖拽式排序的前置交互。直接修改 options 数组以动态拼接序号(如 "Label-1")虽可行,却会触发全量 options 重新计算与 Diff,显著降低大列表场景下的响应性能。
理想的解法是分离数据源与视图表达:用 useRef 缓存原始不可变选项(避免重复创建),用 useState 管理当前带序号的渲染态 options,并通过 tagRender 自定义选中标签,确保序号在关闭下拉时仍清晰可见。
以下是推荐实现方案(基于 Ant Design v5+):
import { Select, Tag } from 'antd';
import React, { useState, useRef, useMemo } from 'react';
type Option = { label: string; value: string };
const OrderAwareMultiSelect = () => {
// ✅ 原始选项 —— 用 useRef 静态持有,永不变更
const optionsRef = useRef<option>([
{ label: 'Label', value: '1' },
{ label: 'Label', value: '2' },
{ label: 'Label', value: '3' },
{ label: 'Label', value: '4' },
]);
// ✅ 当前选中值序列(保持插入顺序)
const selectedValuesRef = useRef<string>([]);
// ✅ 渲染态 options:label 动态追加序号(如 "Label-1")
const [renderOptions, setRenderOptions] = useState<option>(optionsRef.current);
// ? 核心逻辑:选中/取消时更新序号
const handleChange = (values: string[]) => {
selectedValuesRef.current = values;
// 仅对当前 options 做映射更新,避免深拷贝开销
setRenderOptions(prev =>
prev.map(opt => {
const index = values.indexOf(opt.value);
// 移除旧序号(支持 label 含下划线等场景),再追加新序号
const cleanLabel = opt.label.replace(/-\d+$/, '');
return {
...opt,
label: index === -1 ? cleanLabel : `${cleanLabel}-${index + 1}`,
};
})
);
};
// ?️ 自定义 Tag 渲染:确保关闭面板时仍显示正确序号
const tagRender = ({ value, label, closable, onClose }: any) => {
const option = optionsRef.current.find(o => o.value === value);
const index = selectedValuesRef.current.indexOf(value);
return (
<tag onmousedown="{e"> {
e.preventDefault();
e.stopPropagation();
}}
closable={closable}
onClose={onClose}
style={{ marginRight: 4 }}
>
{`${option?.label || label}-${index + 1}`}
</tag>
);
};
return (
<select mode="multiple" allowclear style="{{" width: placeholder="请选择(序号反映选择顺序)" options="{renderOptions}" onchange="{handleChange}" tagrender="{tagRender}" label showsearch="{false}" filteroption="{false}"></select>
);
};
export default OrderAwareMultiSelect;</option></string>
</option>
✅ 关键设计说明:
- 性能保障:原始 optionsRef 不参与状态更新,renderOptions 仅做浅层 map 映射,无深层遍历或冗余对象创建;
- 序号一致性:tagRender 与下拉 options 共享同一套序号逻辑,杜绝视觉错位;
- 标签防误操作:onMouseDown.preventDefault() 阻止点击 Tag 时意外展开下拉框;
- 序号鲁棒性:正则 /-\d+$/ 精准移除末尾序号,兼容 label 本身含连字符的场景(如 "User-Admin" → "User-Admin-1")。
⚠️ 注意事项:
- 若原始 options 来自异步请求,请在数据就绪后一次性初始化 optionsRef,切勿在渲染中赋值;
- 如需支持搜索(showSearch),需同步改造 filterOption 逻辑,使其基于 cleanLabel 匹配;
- 对于超大数据集(>1000 项),建议结合虚拟滚动(如 rc-virtual-list)进一步优化。
该方案在保证交互直观性的同时,兼顾了性能与可维护性,是 Ant Design 多选序号化需求的生产就绪实践。










