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
相关产品推荐
相关产品推荐

