React中如何通过上下方向键切换下拉菜单选项的active类
React下拉菜单方向键切换选中状态解决方案
原代码核心问题
- 按键判断逻辑错误:
e.key === 38 || e.key === 40混淆了keyCode和key属性的用法,方向键上的e.key值为ArrowUp、下为ArrowDown,38、40是已经废弃的e.keyCode属性值 - 事件绑定对象错误:li标签默认不可聚焦,按键事件无法触发到li元素上,事件应该绑定到全局或者下拉外层容器
- 无方向切换逻辑:原代码没有区分上下方向的移动逻辑,只是粗暴替换当前active类,无法实现上下遍历
- 类名拼接错误:
this.className += "active"没有加空格,会导致类名拼接为list-itemactive,无法匹配active样式 - useEffect缺少依赖项:无依赖的useEffect会在每次组件渲染后重复执行,导致事件重复绑定,且未处理下拉关闭时的事件解绑,会造成内存泄漏
- 重复设置默认选中:循环内多次给第一个元素加active类,属于无效冗余逻辑
修复后的实现方案(符合React最佳实践,不直接操作DOM)
1. 新增状态管理选中索引
// 新增activeIndex状态,默认值0对应第一个选项 const [activeIndex, setActiveIndex] = useState(0); // 原有状态保留 const [open, setOpen] = useState(false); const [selected, setSelected] = useState(""); const options = ["Option1", "Option2", "Option3", "Option4"];
2. 新增键盘事件监听useEffect
useEffect(() => { // 只有下拉打开时才绑定键盘事件 if (!open) return; // 每次打开下拉重置选中索引为第一个 setActiveIndex(0); const handleKeyDown = (e) => { if (e.key === 'ArrowDown') { // 阻止默认页面滚动 e.preventDefault(); setActiveIndex(prev => prev < options.length - 1 ? prev + 1 : prev); } else if (e.key === 'ArrowUp') { e.preventDefault(); setActiveIndex(prev => prev > 0 ? prev - 1 : prev); } else if (e.key === 'Enter') { // 可选功能:按回车选中当前项 setSelected(options[activeIndex]); setOpen(false); } } window.addEventListener('keydown', handleKeyDown); // 卸载/下拉关闭时移除事件监听 return () => window.removeEventListener('keydown', handleKeyDown); }, [open, options.length, activeIndex])
3. 修改下拉列表渲染逻辑,动态绑定active类
{open && ( <ul className="list" id="lists"> {options.map((option, i) => ( <li onClick={() => { setSelected(option); setOpen(false); }} {/* 动态判断是否添加active类 */} className={`list-item ${i === activeIndex ? 'active' : ''}`} key={i} > {option} </li> ))} </ul> )}
内容的提问来源于stack exchange,提问作者Gunal Bondre
相关产品推荐
相关产品推荐

