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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 23:50:29