react+ts 造轮子仿antd Menu组件(升级版)
Posted coderlin_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react+ts 造轮子仿antd Menu组件(升级版)相关的知识,希望对你有一定的参考价值。
Menu
之前做了一个简单版的Menu组件,然后对这个组件升级了一下,使其支持下拉菜单功能。看效果图
横向的下拉菜单鼠标经过即打开,鼠标离开即关闭。纵向的下拉菜单是鼠标点击即打开,再点击一次即关闭。
继上一篇初级版后,我们需要多一个组件,SubMenu组件,这个组件的本质也是一个li标签,然后里面放着一个div和一个ul,div用来放标题,ul用来放下拉菜单的组件,所以SubMenu组件里面放着的也是MenuItem组件。
看代码:
接口
组件代码
import React, useState, useContext from 'react'
import MenuContext from './conText'
import cx from 'classnames'
export interface ISubMenu
className?: string,
index?: string,
title?: string,
const SubMenu: React.FC<ISubMenu> = (props) =>
const className, index, title, children = props
const value = useContext(MenuContext);
const isOpen = index? (value.defaultOpenSubMenu as Array<string>).includes(index) : false
const [menuOpen, setmenuOpen] = useState(isOpen);
const classes = cx('menu-item submenu-item', className, 'menu-submeu-open':menuOpen)
const renderChildren = () =>
const childrenElement = React.Children.map(children, (child, i) =>
const chlidElement = child as React.FunctionComponentElement<ISubMenu>
if (chlidElement?.type?.displayName !== 'MenuItem') //传入的子组件不是MenuItem就报错
console.error('Waring: Menu has a child which is not a MenuItem')
else
return React.cloneElement(chlidElement,
index: `$index-$i`
)
)
return (
<ul className="menu-submenu">
childrenElement
</ul>
)
const handleClick = (e: React.MouseEvent)=> //竖向鼠标点击显示
e.preventDefault()
setmenuOpen(!menuOpen)
let timer:any //防抖函数
const handleMouse = (e: React.MouseEvent, toggle: boolean) => //横向鼠标经过即显示
clearTimeout(timer)
e.preventDefault()
timer = setTimeout(()=>
setmenuOpen(toggle)
,50)
const clickEvents = value.type === 'vertical'?
onClick:(e: React.MouseEvent)=>handleClick(e)
:
const MouseEvents = value.type !== 'vertical'?
onMouseEnter:(e: React.MouseEvent)=>handleMouse(e, true),
onMouseLeave:(e: React.MouseEvent)=>handleMouse(e, false)
:
return (
<li key=index className=classes ...MouseEvents>
<div className="submenu-title" ...clickEvents>title</div>
renderChildren()
</li>
)
SubMenu.displayName = 'SubMenu'
export default SubMenu
一步一步看,首先先看结构
本质也是一个li标签,再看
这个跟我们初级版的也是一样的,因为我们要判断SubMenu里面包含的是不是MenuItem标签,并且必须手动给他加上index值,这个index值是返回给回调函数的并且用于判断当前这个MenuItem是不是处于active状态。由于我们外层用了索引值Index来做index的值,所以如果在这里继续用Index值的话,将会与外面冲突。所以我们让Index值为string,并且传入另一种数据结构的index值给他,
第一个index是外层Menu时,给每个儿子加上的index索引值,i是SubMenu遍历他的子组件时的index值,换种写法而已。所以我们下拉菜单的索引值会变成2-1,2-2等这种。
接着是点击或者经过显示下拉菜单的方法
先写两个函数,分别对应点击显示和鼠标经过显示。设置一个useState值,用来判断下拉菜单是否关闭。鼠标经过时,当我们鼠标高速移动时容易造成事件多次触发,所以采用了防抖函数的思维,创建一个timer。用来判断。
然后就是判断当为纵向时,点击事件成立,当不为纵向时,即横向时,鼠标事件成立。然后通过解构赋值,鼠标经过事件放在外层li上,点击事件放在div上,这样,当type为横向时,只有鼠标事件有效,当type为纵向时,只有点击事件有效。
设置默认打开
下拉菜单可能有多个,而且有的是要默认打开的,可以这样设置,
通过用户传入的一个数组,在通过判断当前下拉菜单的索引值是否在这个数组里面,在的话就默认显示,不在的话就默认不显示。这个数组由用户传给Menu组件,Menu组件通过context来共享,看代码
刚才说过了这个index,是Menu组件遍历子组件是自动加上去的,然后通过共享context的数据拿到数组,注意我们定义的时候用了?
即它的值可能为undefined,所以我们必须做类型断言,告诉编译器我们这个东西就是一个字符串数组,这样编写代码的时候就不会报错,通过判断Index是否在这个数组里面,在一开始设置是否默认打开的时候,根据这个判断来设置,如果是的话一开始就为true,默认打开。
Menu组件的基本功能就算完成了,后续自己可以根据需求或者思路来加上不同的多样的功能。
以上是关于react+ts 造轮子仿antd Menu组件(升级版)的主要内容,如果未能解决你的问题,请参考以下文章
Antd Menu组件应该如何结合react-router Link组件?