You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

react-bootstrap-typeahead如何实现同一选项多次独立选择

Typeahead 重复选择同选项且支持单独删除的实现方案

核心问题出在组件默认的选中项匹配逻辑:组件默认以选项的id/label值作为唯一判定依据,相同id的选项会被识别为同一个选中项,所以删除时会把同id的项全部移除。

实现思路

不要直接把原始选项对象存入选中状态,每次选中选项时,给当前选中的条目生成一个唯一的临时实例ID,保证每一个选中项哪怕原始业务属性完全一致,也有独立的唯一标识,从根源上避免组件把重复选中的项判定为同一个条目。

具体实现步骤

  • 初始化选中状态时,给所有初始选中项追加唯一临时实例ID,不要直接使用原始选项对象的引用。
  • 设置filterBy={() => true}关闭组件默认的已选项过滤逻辑,让已经选中过的选项始终保留在下拉可选列表中,支持重复选择。
  • 改造onChange回调:新增选项时,给新加入的选中项补全唯一实例ID;删除操作时依托唯一实例ID做精准匹配。
  • 兜底处理:如果组件默认的标签删除逻辑仍然存在批量删除问题,通过renderToken自定义选中标签的渲染,自己绑定删除事件,按实例ID精准移除对应项。

完整示例代码

import { useState } from 'react';
import { Typeahead } from 'react-bootstrap-typeahead';

// 生成唯一临时ID的工具函数,有uuid库可直接替换为uuidv4()
const generateInstanceId = () => `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;

const SkillSelector = ({ currentSpotSkills, availableSkills }) => {
  // 初始化时给所有初始选中项加唯一实例ID
  const [multiSelections, setMultiSelections] = useState(() =>
    currentSpotSkills.map(skill => ({ ...skill, _instanceId: generateInstanceId() }))
  );

  const handleSelectionChange = (newSelectedList) => {
    const prevCount = multiSelections.length;
    const newCount = newSelectedList.length;

    // 判定为新增选中项时,给新项补唯一ID
    if (newCount > prevCount) {
      const newItem = newSelectedList[newCount - 1];
      if (!newItem._instanceId) {
        newSelectedList[newCount - 1] = {
          ...newItem,
          _instanceId: generateInstanceId()
        };
      }
    }

    setMultiSelections(newSelectedList);
  };

  return (
    <Typeahead
      id="basic-typeahead-multiple"
      labelKey="skill"
      multiple
      onChange={handleSelectionChange}
      options={availableSkills.slice().sort()}
      placeholder="Rôle..."
      selected={multiSelections}
      filterBy={() => true}
      // 兜底自定义选中标签,彻底规避默认删除逻辑的匹配问题
      renderToken={(option, _, index) => (
        <div key={option._instanceId} className="rbt-token">
          {option.skill}
          <button
            type="button"
            className="rbt-token-remove-button"
            onClick={() => {
              setMultiSelections(prev =>
                prev.filter(item => item._instanceId !== option._instanceId)
              );
            }}
          >
            ×
          </button>
        </div>
      )}
    />
  );
};

注意事项

  • 禁止修改原始availableSkills数据源,唯一实例ID只追加到multiSelections里的选中项副本上,避免污染全局选项数据。
  • 组件的labelKey保持原有配置指向skill字段即可,新增的_instanceId字段不会影响选项文本的正常显示。
  • 如果业务上后续需要提交选中的技能数据,提交前把每个选中项的_instanceId字段剔除即可,不会影响原有业务数据结构。

内容的提问来源于stack exchange,提问作者minioim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 04:18:20