Menu

Menu組件大家還是比較熟悉的,可以切換菜單欄的一種,先看初級版的Menu效果圖

react ant design 菜單默認展開_ide

組件分析:
樣式有四種,默認是Primary,還有danger,success,warning,
然後有兩種排序,上下或者左右。
點擊可以切換。

接口

react ant design 菜單默認展開_共享數據_02


一共有三個,最後一個是來實現父子組件的數據共享。

第一個為Menu組件,本質上就是一個ul標籤,可以傳入的值當前高亮的索引,類型,樣式,onSelect回調函數,類名。

第二個是MenuItem組件,本質上是一個li標籤,可以傳入的值有標明自身索引的值Index,disabled,類名還有style控制樣式。

類型別名

react ant design 菜單默認展開_ide_03

Menu組件代碼

import React, { useState, createContext } from 'react'
import cx from 'classnames'
import { IMenuProps, menuType, IMenuContext, IMenuItemProps } from './common'
import { MenuContext } from './conText'



const Menu: React.FC<IMenuProps> = (props) => {
    const { style, color, className, activeIndex, type, onSelect, children, ...restProps } = props
    
    const classes = cx(
        'menu', 
        className, 
        {   [`menu-${type}`]: type,
            [`menu-${style}`]:style,
            [`menu-horizontal`]:Boolean(type!=='vertical')
        })
    const [index, setindex] = useState(activeIndex)
    const handle = (index: number) => {
        setindex(index)
        if (onSelect) {
            onSelect(index)
        }
    }
    const value: IMenuContext = {
        activeIndex: index!, //感嘆號非空,強行告訴編譯器,我這個activeInedx不是空的
        onSelect: handle
    }
    //代碼升級
    const renderChildren = () => {
        return React.Children.map(children, (child, index) => {
            const chlidElement = child as React.FunctionComponentElement<IMenuItemProps>
            if (chlidElement?.type?.displayName !== 'MenuItem') {//傳入的子組件不是MenuItem就報錯
                console.error('Waring: Menu has a child which is not a MenuItem')
            } else {
                return React.cloneElement(chlidElement, {
                    //每次都要傳入index,甚是麻煩,cloneElement,可以複製節點並且附加上屬性,前提是IMenuItemProps裏面有定義的
                    index
                })
            }
        })
    }
    return <ul className={classes} {...restProps} data-testid="test-menu">
        <MenuContext.Provider value={value}>
            {renderChildren()}
        </MenuContext.Provider>
    </ul>
}


Menu.defaultProps = {
    activeIndex: 0,
    type: menuType.Horizontal,
}


export default Menu

樣式用屬性選擇器,拼接,默認是menu-horizontal,也就是橫着來排序。

使用Context來共享數據,

react ant design 菜單默認展開_共享數據_04


react ant design 菜單默認展開_ide_05


創建共享數據

react ant design 菜單默認展開_共享數據_06


第二個值是函數,子組件點擊後會回傳index回來,父組件再更新index的值,然後再調用外面傳入的onSelect函數,將index值傳進去。

react ant design 菜單默認展開_高亮_07


這裏用了一個函數

react ant design 菜單默認展開_高亮_08


這個函數的主要作用是判斷傳進來的children是不是MenuItem,如果不是即報錯,是的話,返回一個cloneElement,這個方法可以複製我們傳入的childElement,並且給他附上一個屬性,index,這樣創建出來的li標籤上都有一個index屬性。不然我們調用時只能手動添加,不方便。

react ant design 菜單默認展開_ide_09


因為我們是通過這個index的值來判斷當前是否高亮,所以他是必須的。

MenuItem組件代碼

import React, { useContext } from 'react'
import cx from 'classnames'
import {MenuContext}  from './conText'
import {IMenuItemProps} from './common'

const MenuItem: React.FC<IMenuItemProps> = (props) => {
    const {index, classNames, disabled, style, children, ...restProps } = props
    const value = useContext(MenuContext);
    const {activeIndex, onSelect} = value
    const classes = cx('menu-item',classNames,{'menu-item-disabled':disabled,'menu-item-active':Boolean(activeIndex===index)})
    const handleClick = () => {
        if(onSelect && !disabled &&(typeof index === 'number')){
            onSelect(index)
        }
    }
    return (
        <li className={classes} style= {style} onClick={handleClick} {...restProps}>{children}</li>
    )
}
MenuItem.defaultProps = {
    disabled:false
}
MenuItem.displayName = 'MenuItem'
export default MenuItem

這裏就很容易了,判斷高亮只需判斷父組件傳入的index,與自身的index值是否相同即可。回調的話只是將index值通過父親傳入的handle也就是onSelect函數傳出去,達到實時改變Index值的效果。

樣式

react ant design 菜單默認展開_共享數據_10


react ant design 菜單默認展開_共享數據_11


分成兩個,互不影響。

調用

react ant design 菜單默認展開_高亮_12