React点符号组件传值:如何将Dropdown.Title的openDropdown传给Menu?
解决方案:通过React Context共享Dropdown组件状态
当前Dropdown.Title的状态仅局限于自身,同级的Dropdown.Menu无法直接获取该状态。我们可以将状态提升到共同的父组件Dropdown中,结合React Context实现子组件间的状态共享,既能保留点符号的组件写法,又无需在外部使用时额外维护状态逻辑。
1. 创建状态管理的Context文件 (openDropdown.tsx)
该文件负责定义Context、管理状态,并提供供子组件调用的Hook:
import { createContext, useContext, useState, ReactNode } from 'react'; // 定义Context的类型约束 type DropdownContextType = { open: boolean; toggle: () => void; }; // 创建Context,默认值用于类型提示 const DropdownContext = createContext<DropdownContextType | undefined>(undefined); // 自定义Hook,简化Context的获取并做合法性校验 export const useDropdown = () => { const context = useContext(DropdownContext); if (!context) { throw new Error('useDropdown 必须在 DropdownProvider 内部使用'); } return context; }; // Provider组件,封装状态逻辑 export const DropdownProvider = ({ children }: { children: ReactNode }) => { const [open, setOpen] = useState(false); const toggle = () => { setOpen(prev => !prev); }; return ( <DropdownContext.Provider value={{ open, toggle }}> {children} </DropdownContext.Provider> ); };
2. 改造主Dropdown组件
让主组件包裹DropdownProvider,确保内部所有子组件都能访问到Context:
import { DropdownProvider } from './openDropdown'; import DropdownTitle from './DropdownTitle'; import DropdownMenu from './DropdownMenu'; import DropdownItem from './DropdownItem'; import DropdownDivider from './DropdownDivider'; const Dropdown = ({ children, className }: { children: ReactNode; className?: string }) => { return ( <div className={className}> <DropdownProvider> {children} </DropdownProvider> </div> ); }; // 挂载子组件,保持点符号调用的写法 Dropdown.Title = DropdownTitle; Dropdown.Menu = DropdownMenu; Dropdown.Item = DropdownItem; Dropdown.Divider = DropdownDivider; export default Dropdown;
3. 修改Dropdown.Title组件
移除自身的状态,直接从Context获取切换方法:
import { useDropdown } from './openDropdown'; const DropdownTitle = ({ children, className }: { children: ReactNode; className?: string }) => { const { toggle } = useDropdown(); return ( <button type="button" onClick={toggle} className={className} > {children} </button> ); }; export default DropdownTitle;
4. 修改Dropdown.Menu组件
从Context获取状态,控制菜单的显示隐藏:
import { useDropdown } from './openDropdown'; const DropdownMenu = ({ children, align, className }: { children: ReactNode; align?: string; className?: string }) => { const { open } = useDropdown(); // 示例:根据状态和align属性拼接样式类 const menuClasses = `${className || ''} ${align === 'end' ? 'justify-end' : ''} ${open ? 'block' : 'hidden'}`; return ( <div className={menuClasses}> {children} </div> ); }; export default DropdownMenu;
最终使用方式
和你原本的写法完全一致,无需额外处理状态逻辑:
<Dropdown className="float-right"> <Dropdown.Title>Open menu</Dropdown.Title> <Dropdown.Menu align="end"> <Dropdown.Item className="hello mate" href="/dashboard"> Dashboard </Dropdown.Item> <Dropdown.Item className="hello mate" href="/settings"> Settings </Dropdown.Item> <Dropdown.Divider /> <Dropdown.Item className="hello mate" onClick={signOut}> Sign out </Dropdown.Item> </Dropdown.Menu> </Dropdown>
内容的提问来源于stack exchange,提问作者Mo.
相关产品推荐
相关产品推荐

