Menu
Menu組件大家還是比較熟悉的,可以切換菜單欄的一種,先看初級版的Menu效果圖
組件分析:
樣式有四種,默認是Primary,還有danger,success,warning,
然後有兩種排序,上下或者左右。
點擊可以切換。
接口
一共有三個,最後一個是來實現父子組件的數據共享。
第一個為Menu組件,本質上就是一個ul標籤,可以傳入的值當前高亮的索引,類型,樣式,onSelect回調函數,類名。
第二個是MenuItem組件,本質上是一個li標籤,可以傳入的值有標明自身索引的值Index,disabled,類名還有style控制樣式。
類型別名
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來共享數據,
創建共享數據
第二個值是函數,子組件點擊後會回傳index回來,父組件再更新index的值,然後再調用外面傳入的onSelect函數,將index值傳進去。
這裏用了一個函數
這個函數的主要作用是判斷傳進來的children是不是MenuItem,如果不是即報錯,是的話,返回一個cloneElement,這個方法可以複製我們傳入的childElement,並且給他附上一個屬性,index,這樣創建出來的li標籤上都有一個index屬性。不然我們調用時只能手動添加,不方便。
因為我們是通過這個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值的效果。
樣式
分成兩個,互不影響。
調用