实现TagsInput组件支持上下方向键与回车键选择标签功能
问题背景
现有TagsInput组件已实现输入字符匹配建议、鼠标选择标签功能,但无法通过上下方向键切换建议选项,也不能用回车键确认选择,需补充该交互功能。
修改后的完整代码
import { useState } from 'react'; import ClearIcon from '@mui/icons-material/Clear'; // 假设classes是导入的样式模块,根据实际项目调整 import classes from './TagsInput.module.css'; export default function TagsInput(props) { const { tags, setTags, tagSuggestions } = props; const [input, setInput] = useState(""); const [suggestedTags, setSuggestedTags] = useState([]); const [isValid, setIsValid] = useState(true); // 新增状态:跟踪当前选中的建议项索引 const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(-1); const onChange = (e) => { const { value } = e.target; setInput(value); if (value.length < 1) { setSuggestedTags([]); setIsValid(true); setSelectedSuggestionIndex(-1); return; } const matchedSuggestions = tagSuggestions.filter(s => s.startsWith(value) && !tags.includes(s) ); setSuggestedTags(matchedSuggestions); // 输入有匹配项时,默认选中第一个 setSelectedSuggestionIndex(matchedSuggestions.length > 0 ? 0 : -1); }; const onKeyDown = (e) => { const { key } = e; const trimmedInput = input.trim(); // 处理上方向键 if (key === "ArrowUp") { e.preventDefault(); setSelectedSuggestionIndex(prev => { const newIndex = prev - 1; // 边界循环:到顶部后跳转到最后一项 return newIndex >= 0 ? newIndex : suggestedTags.length - 1; }); return; } // 处理下方向键 if (key === "ArrowDown") { e.preventDefault(); setSelectedSuggestionIndex(prev => { const newIndex = prev + 1; // 边界循环:到底部后跳转到第一项 return newIndex < suggestedTags.length ? newIndex : 0; }); return; } // 处理回车键 if (key === "Enter") { e.preventDefault(); // 优先选中当前高亮的建议项 if (selectedSuggestionIndex !== -1 && suggestedTags.length > 0) { selectTag(suggestedTags[selectedSuggestionIndex]); } else if (trimmedInput.length && !tags.includes(trimmedInput)) { // 无选中项时,将输入内容作为新标签添加 setTags(prevState => [...prevState, trimmedInput]); setInput(""); setSuggestedTags([]); setSelectedSuggestionIndex(-1); } return; } // 保留原空格添加标签逻辑(按需保留) if (key === " ") { e.preventDefault(); if (trimmedInput.length && !tags.includes(trimmedInput)) { setTags(prevState => [...prevState, trimmedInput]); setInput(""); setSuggestedTags([]); setSelectedSuggestionIndex(-1); } } }; const deleteTag = (index) => { setTags(prevState => prevState.filter((tag, i) => i !== index)); }; const selectTag = (tag) => { setTags(prevState => [...prevState, tag]); setSuggestedTags([]); setInput(""); setSelectedSuggestionIndex(-1); }; return ( <div className={classes.container}> {tags.map((tag, index) => ( <div className={classes.tag} key={tag}> <ClearIcon className={classes.del} fontSize="big" onClick={() => deleteTag(index)} /> {tag} </div> ))} <input className={classes.input} value={input} placeholder={props.inputPlaceholder} onKeyDown={onKeyDown} onChange={onChange} /> {suggestedTags.length ? ( <div className={classes.tagSuggestionWrapper}> {suggestedTags.map((tag, index) => ( <div key={tag} className={`${classes.tagSuggestion} ${index === selectedSuggestionIndex ? classes.tagSuggestionActive : ''}`} onClick={() => selectTag(tag)} > {tag} </div> ))} </div> ) : null} </div> ); }
关键修改说明
- 新增选中状态:添加
selectedSuggestionIndex状态,记录当前通过方向键选中的建议项索引,初始值-1表示未选中任何项。 - 修正语法错误:原代码中
onKeyDown函数嵌套在onChange内,导致逻辑无法正常执行,已将其移出作为独立函数。 - 方向键交互逻辑:监听
ArrowUp和ArrowDown事件,调整选中索引并处理边界循环(比如按上键到第一项后,再按会跳转到最后一项)。 - 回车键交互逻辑:优先选中当前高亮的建议项;若无选中项且输入内容合法,则将输入内容添加为新标签。
- 选中项高亮:渲染建议项时,根据
selectedSuggestionIndex为当前选中项添加高亮样式类(需在样式文件中定义.tagSuggestionActive,比如设置背景色区分选中状态)。 - 状态同步重置:在清空输入、选中标签等操作时,同步重置选中索引和建议列表状态,避免残留状态影响交互。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

