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

如何改造自定义React Dropdown组件实现Body的显隐控制?

改造Dropdown组件的两种可行方案

方案一:React Context 状态共享(推荐)

这是react-bootstrap同款实现思路,能完美支持任意层级的子组件共享Dropdown状态,适合复杂嵌套场景。

  1. 先创建Dropdown专属Context
import { createContext, useContext, useState, ReactNode } from 'react';

// 定义上下文数据结构
const DropdownContext = createContext<{
  isOpen: boolean;
  toggleOpen: () => void;
} | null>(null);

// 封装自定义Hook,简化子组件调用
const useDropdown = () => {
  const context = useContext(DropdownContext);
  if (!context) {
    throw new Error('Dropdown.Header/Dropdown.Body必须在Dropdown组件内部使用');
  }
  return context;
};
  1. 改造Dropdown父组件,提供上下文
type DropdownProps = {
  children: ReactNode;
  defaultOpen?: boolean;
};

const Dropdown = ({ children, defaultOpen = false }: DropdownProps) => {
  const [isOpen, setIsOpen] = useState(defaultOpen);
  const toggleOpen = () => setIsOpen(prev => !prev);

  return (
    <DropdownContext.Provider value={{ isOpen, toggleOpen }}>
      <div className="dropdown-container">{children}</div>
    </DropdownContext.Provider>
  );
};
  1. 实现Header和Body组件
// Header组件无需状态,直接渲染内容
const DropdownHeader = ({ children }: { children: ReactNode }) => {
  return <div className="dropdown-header">{children}</div>;
};

// Body组件通过Hook获取isOpen控制显示
const DropdownBody = ({ children }: { children: ReactNode }) => {
  const { isOpen } = useDropdown();
  return isOpen ? <div className="dropdown-body">{children}</div> : null;
};

// 挂载到Dropdown上,保持调用一致性
Dropdown.Header = DropdownHeader;
Dropdown.Body = DropdownBody;
Dropdown.Toggle = ({ children }: { children: ReactNode }) => {
  const { toggleOpen } = useDropdown();
  return <button onClick={toggleOpen} className="dropdown-toggle">{children}</button>;
};
  1. 使用示例
<Dropdown>
  <Dropdown.Toggle>点击展开菜单</Dropdown.Toggle>
  <Dropdown.Header>用户操作菜单</Dropdown.Header>
  <Dropdown.Body>
    <div>
      <p>可以放任意React元素</p>
      <button>修改资料</button>
      <button>退出登录</button>
    </div>
  </Dropdown.Body>
</Dropdown>

方案二:克隆子组件传递Props(适合简单场景)

如果你的Dropdown结构固定(Toggle/Header/Body直接作为Dropdown子元素),可以直接通过克隆子组件传递状态,无需Context。

  1. 改造Dropdown父组件
const Dropdown = ({ children, defaultOpen = false }: DropdownProps) => {
  const [isOpen, setIsOpen] = useState(defaultOpen);
  const toggleOpen = () => setIsOpen(prev => !prev);

  // 遍历子组件,给对应类型传递props
  const renderedChildren = React.Children.map(children, child => {
    if (!React.isValidElement(child)) return child;
    
    if (child.type === DropdownBody) {
      return React.cloneElement(child, { isOpen });
    }
    if (child.type === Dropdown.Toggle) {
      return React.cloneElement(child, { onClick: toggleOpen });
    }
    return child;
  });

  return <div className="dropdown-container">{renderedChildren}</div>;
};
  1. 调整Dropdown.Body组件
const DropdownBody = ({ children, isOpen }: { children: ReactNode; isOpen: boolean }) => {
  return isOpen ? <div className="dropdown-body">{children}</div> : null;
};

方案对比

  • Context方案:灵活性拉满,支持任意嵌套层级,是生产环境首选。
  • 克隆子组件方案:实现简单,但仅支持扁平结构的子组件,扩展性差。

内容的提问来源于stack exchange,提问作者Iavor Orlyov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 14:27:23