一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Ant Design 多选组件中实现选择顺序并展示的完整方案

时间:2026-07-27 16:07:47 编辑:袖梨 来源:一聚教程网

本文详解如何在 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}    />  );}

⚠️ 关键注意事项

  • 不要直接修改 options 数组引用:每次 onChange 都应生成新数组(如 map 返回新项),否则 Ant Design 无法触发重新渲染。
  • 序号清理逻辑要健壮:使用正则 /-d+$/ 替换末尾序号,而非简单拼接,避免出现 "Label-1-2" 这类错误。
  • tagRender 必须独立计算序号:它不依赖 renderOptions,而是直接查 selectedValues.current,确保标签序号绝对准确(即使 renderOptions 更新有延迟)。
  • 性能优化点
    • optionsRef 存储原始选项,避免 handleChange 闭包捕获过期 options;
    • selectedValues 用 useRef 而非 useState,避免无意义的 re-render;
    • renderOptions 仅在必要时更新(即 onChange 触发时),且 map 操作复杂度为 O(n),n 为选项总数(通常较小)。

✅ 效果验证

  • 初始:所有选项显示为 Label;
  • 选中 value="2" → 下拉中该选项变为 Label-1,顶部标签显示 Label-1;
  • 再选中 value="1" → Label 变为 Label-2,Label-1 保持不变;
  • 取消选中 value="2" → 原 Label-1 恢复为 Label,原 Label-2 变为 Label-1;
  • 任意顺序增删,序号始终连续、从 1 开始。

该方案兼顾可读性、性能与 Ant Design 最佳实践,适用于中大型表单系统。如需支持搜索过滤后仍保持序号,可进一步封装 filterOption 逻辑,但需注意过滤后的序号仅对可见项生效——这属于进阶需求,可根据实际场景权衡。

热门栏目