react+ts 造轮子仿antd Menu组件(初级版)
Posted coderlin_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react+ts 造轮子仿antd Menu组件(初级版)相关的知识,希望对你有一定的参考价值。
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值的效果。
样式
分成两个,互不影响。
调用
以上是关于react+ts 造轮子仿antd Menu组件(初级版)的主要内容,如果未能解决你的问题,请参考以下文章
Antd Menu组件应该如何结合react-router Link组件?