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值的效果。
样式
分成两个,互不影响。
调用