本文详解如何在 Ant Design 的 Select[mode="multiple"] 中动态显示用户的选择顺序(如“Label-1”、“Label-2”),并在标签(Tag)和下拉选项中同步更新序号,同时避免高频重渲染性能问题。
本文详解如何在 ant design 的 `select[mode="multiple"]` 中动态显示用户的选择顺序(如“label-1”、“label-2”),并在标签(tag)和下拉选项中同步更新序号,同时避免高频重渲染性能问题。
在 Ant Design 的多选组件中,默认仅维护选中值的集合(string[] 或 number[]),不保留选择顺序信息。但实际业务中常需向用户明确传达“谁先被选中”,例如配置优先级、定义执行序列等场景。此时,简单地将 label 静态写死为 "Label-1" 不可行——因为顺序会随增删动态变化,且下拉列表与已选标签需保持一致。
核心挑战在于:既要实时反映序号,又不能因每次 onChange 都重建整个 options 数组导致性能下降或闪烁。解决方案是结合 useRef 缓存原始选项、useState 管理带序号的渲染态选项,并通过 tagRender 自定义已选标签的显示逻辑。
import { Select, Tag } from 'antd';import type { SelectProps, CustomTagProps } from 'antd/es/select';const { Option } = Select;// 1. 原始静态选项(永不变更,用 useRef 避免闭包捕获旧值)const initialOptions: SelectProps['options'] = [ { label: 'Label', value: '1' }, { label: 'Label', value: '2' }, { label: 'Label', value: '3' }, { label: 'Label', value: '4' },];export default function OrderedMultiSelect() { const selectedValues = useRef<string[]>([]); const optionsRef = useRef(initialOptions); // 2. 渲染态选项(含动态序号),受 selection 控制 const [renderOptions, setRenderOptions] = useState<SelectProps['options']>(initialOptions); const handleChange = (values: string[]) => { selectedValues.current = values; // 仅对 options 进行映射更新:提取基础 label,追加当前序号(从 1 开始) setRenderOptions(prev => prev.map(opt => { const index = values.indexOf(opt.value); // 移除旧序号(如 "_3"),防止叠加;支持任意 label 格式 const baseLabel = opt.label.replace(/_d+$/, ''); return { ...opt, label: index === -1 ? baseLabel : `${baseLabel}-${index + 1}`, }; }) ); }; // 3. 自定义 tagRender —— 确保已选标签始终显示正确序号(即使 options 未重渲染) const tagRender = ({ value, label, closable, onClose }: CustomTagProps) => { const option = optionsRef.current.find(o => o.value === value); const index = selectedValues.current.indexOf(value); return ( <Tag onMouseDown={e => { e.preventDefault(); e.stopPropagation(); }} closable={closable} onClose={onClose} style={{ marginRight: 6 }} > {`${option?.label.replace(/-d+$/, '')}-${index + 1}`} </Tag> ); }; return ( <Select mode="multiple" allowClear style={{ width: '100%' }} placeholder="请选择(按选择顺序编号)" options={renderOptions} onChange={handleChange} tagRender={tagRender} // 可选:禁用下拉内 label 的 hover 高亮闪烁(提升体验) dropdownMatchSelectWidth={false} /> );}
该方案兼顾可读性、性能与 Ant Design 最佳实践,适用于中大型表单系统。如需支持搜索过滤后仍保持序号,可进一步封装 filterOption 逻辑,但需注意过滤后的序号仅对可见项生效——这属于进阶需求,可根据实际场景权衡。