React中如何处理多个Dropdown菜单的激活状态?
解决多下拉菜单同步展开的问题
问题根源在于当前组件使用了单一的active和selectedOption状态,所有通过options.map生成的下拉菜单都共享这两个状态,导致点击任意一个菜单按钮时,所有菜单的状态都会同步变化。
要实现单个菜单独立控制,需要为每个下拉菜单单独维护状态,具体修改如下:
修改后的代码
import React, { useState } from 'react'; import * as style from './Dropdown.module.css'; const Dropdown = (props) => { const { options, id } = props; // 改用对象存储每个下拉菜单的激活状态,键为option的唯一标识 const [activeStates, setActiveStates] = useState({}); // 改用对象存储每个下拉菜单的选中索引,键为option的唯一标识 const [selectedOptions, setSelectedOptions] = useState({}); // 切换指定下拉菜单的激活状态 const handleActive = (optionId) => { setActiveStates(prev => ({ ...prev, [optionId]: !prev[optionId] })); }; const setSelectedThenCloseDropdown = (optionId, index, filter, item) => { filter.handler(item); // 更新当前菜单的选中索引 setSelectedOptions(prev => ({ ...prev, [optionId]: index })); // 关闭当前菜单 setActiveStates(prev => ({ ...prev, [optionId]: false })); }; const handleKeyDown = (optionId, index, filter, item) => (e) => { switch (e.key) { case `Escape`: setActiveStates(prev => ({ ...prev, [optionId]: false })); break; case ' ': case 'SpaceBar': case 'Enter': e.preventDefault(); setSelectedThenCloseDropdown(optionId, index, filter, item); break; default: break; } }; return ( <div className={style.dropdownWrapper} id={id}> {options.map((option) => { const optionId = option.current.id; // 获取当前菜单的激活状态,默认false const isActive = activeStates[optionId] || false; // 获取当前菜单的选中索引,默认0 const selectedIndex = selectedOptions[optionId] || 0; return ( <div key={optionId + option.current.name} className={style.dropdownWrapper}> <div className={style.dropdownContainer}> <button role="button" onClick={() => handleActive(optionId)} aria-haspopup="listbox" aria-expanded={isActive} type="button" className={style.dropdownToggle}> {option.current.name} </button> {isActive && ( <div role="listbox" aria-activedescendant={option.list[selectedIndex]} tabIndex={-1} className={style.dropdownMenu}> {option.list.map((item, index) => { return ( <a role="option" tabIndex={0} onKeyDown={handleKeyDown(optionId, index, option, item)} aria-selected={selectedIndex === index} className={style.dropdownItem} key={item.name + option.current.name} onClick={() => { option.handler(item); setSelectedOptions(prev => ({ ...prev, [optionId]: index })); setActiveStates(prev => ({ ...prev, [optionId]: false })); }}> {item.name} </a> ); })} </div> )} </div> </div> ); })} </div> ); }; export default Dropdown;
关键改动说明
- 状态存储方式变更:将
active和selectedOption从单一值改为对象,用每个下拉菜单的option.current.id作为键,分别存储对应菜单的激活状态和选中索引,实现状态隔离。 - 激活状态切换逻辑:
handleActive函数接收当前菜单的optionId,仅修改该菜单对应的激活状态。 - 菜单显示与ARIA属性:每个菜单的
aria-expanded和显示条件{isActive && ...}都使用当前菜单对应的状态值,不再共用全局状态。 - 选中逻辑更新:选中项时仅更新当前菜单的选中索引,并关闭当前菜单,不影响其他菜单状态。
这样修改后,每个下拉菜单的状态都是独立管理的,点击其中一个只会切换自身的展开/收起状态,不会影响其他菜单。
内容的提问来源于stack exchange,提问作者will
相关产品推荐
相关产品推荐

