自定义Menu组件

自定义Menu组件

第一步:定义props

MenuProps 接口定义了 Menu 组件接收的 props,包括默认高亮项、样式、模式、选择项的回调函数和子组件。

export interface MenuProps{
    defaultIndex?: number;  //高亮
    className?: string;
    mode: MenuMode;   //纵向横向
    style?: React.CSSProperties;
    onSelect?: (selectedIndex:number)=>void; //选择第几项
    children:React.ReactNode;
}

第二步:简单的menu组件实现

const Menu : React.FC<MenuProps> = (props) => {
    const {
        className,
        mode='horizontal',
        style,
        children,
        defaultIndex=0,
        onSelect,
    }=props;
  
    const classes = classNames('menu',className,{
        'menu-vertical':mode==='vertical'
    })

    return (
        <ul className={classes} style={style}>
        {children}    
        </ul>
    )
}

第三步:使用useState控制激活项,添加handleClick 函数用于处理菜单项的点击事件,更新当前激活项并调用 onSelect 回调

const [currentActive,setActive]=useState(defaultIndex);

const handleClick = (index:number) => {
        //1.调用onselect 2.active变化
        setActive(index);
        if(onSelect){
            onSelect(index);
        }
    }

第四步:创建文件menuItem.tsx

完整代码如下

import React ,{useContext} from "react";
import classNames from "classnames";
import { MenuContext } from "./menu.tsx";

export interface MenuItemProps {
    index: number;
    disabled?: boolean;
    className?: string;
    style?: React.CSSProperties;
    children:React.ReactNode;
}
const MenuItem: React.FC<MenuItemProps>=(props)=>{
    const{index,disabled,className,style,children,}=props;
    const context = useContext(MenuContext)  //上下文部分
    const classes=classNames('menu-item',classNames,{
        'is-disabled':disabled,
        'is-active':context.index===index //上下文部分
    })
    const handleClick = () => {
        if(context.onSelect && !disabled){
            context.onSelect(index)
        }
    }
    return(
        <li className={classes} style={style} onClick={handleClick}>
            {children}
        </li>

    )
}
export default MenuItem

第五步:创建上下文

interface IMenuContext {
    index: number; // 当前选中的索引
    onSelect?: (selectedIndex: number) => void; // 选择项的回调
}
export const MenuContext = createContext<IMenuContext>({ index: 0 });

menu组件添加:


    const passedContext : IMenuContext = {
        index: currentActive ? currentActive : 0,
        onSelect: handleClick
    }
    return (
        <ul className={classes} style={style}>
        <MenuContext.Provider value={passedContext}>
        {children}
        </MenuContext.Provider>
        </ul>
    )

passedContext 对象的作用是将 Menu 组件的状态和行为集中在一个地方,以便通过上下文 (MenuContext.Provider) 传递给其子组件。子组件可以通过 useContext(MenuContext) 来访问 indexonSelect,从而实现状态的共享和菜单的交互。

完整版menu.tsx代码如下:

import React,{useState,createContext} from "react";
import classNames from "classnames";

type MenuMode = 'horizontal' | 'vertical'
type SelectCallback = (selectedIndex:number)=>void;

export interface MenuProps{
    defaultIndex?: number;  //高亮
    className?: string;
    mode: MenuMode;   //纵向横向
    style?: React.CSSProperties;
    onSelect?: (selectedIndex:number)=>void; //选择第几项
    children:React.ReactNode;
}

//createContext 是 React 中用于创建上下文的一个 API。
//上下文提供了一种方式,可以在组件树中传递数据,而不必通过每一层组件手动传递 props。
//这样非常适合于共享全局数据,比如用户身份、主题设置或者其他需要在多个组件中共享的状态。
interface IMenuContext{
     index: number;
     onSelect?: (selectedIndex:number)=>void;

}
export const MenuContext = createContext<IMenuContext>({index:0})

const Menu : React.FC<MenuProps> = (props) => {
    const {
        className,
        mode='horizontal',
        style,
        children,
        defaultIndex=0,
        onSelect,
    }=props;
    const [currentActive,setActive]=useState(defaultIndex);
    const classes = classNames('menu',className,{
        'menu-vertical':mode==='vertical'
    })
    const handleClick = (index:number) => {
        //1.调用onselect 2.active变化
        setActive(index);
        if(onSelect){
            onSelect(index);
        }
    }
    const passedContext : IMenuContext = {
        index: currentActive ? currentActive : 0,
        onSelect: handleClick
    }

    return (
        <ul className={classes} style={style}>
        <MenuContext.Provider value={passedContext}>
        {children}
        </MenuContext.Provider>
            
        </ul>
    )
}


export default Menu
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值