react-contexify 右键菜单动态生成

react-contexify右键菜单动态生成

如果菜单数量少,并且没有其他地方需要共用菜单列表,建议参考react-contexify的右键菜单属性disabled不起作用
此文中处理方法相对快捷

关于为何此插件无法根据state动态更新上文中作者已经说明,这里不在赘述

此方法是根据需要展开右键菜单栏目搭配数量无限增加

的数量,性能会不会有问题也没有过多测试,我实际情况就6种组合,不存在太大问题

首先增加菜单数组中的字段用来判断是否显示/禁用

一般情况大概这样

const menu = {
    menuId: '2',
    items: [
      { key: 1, name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
      { key: 2, name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
      { key: 3, name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
      { key: 4, name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
      { key: 5, name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
      { key: 6, name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
    ],
  };
  const ContextMenu = () => (
    <Menu id={menu.menuId}>
      {roleInfoMenu.items.map(item => (
        // 这里需要加key,不然要报错
        <Item key={item.key} onClick={item.handler}>{item.name}</Item>
      ))}
    </Menu>
  );

  const { show } = useContextMenu({
    id: '2',
  });
  const handleContextMenu = (event, node) => {
    event.preventDefault();
    show(event,{
      props: node,
    });
  };

  const rightContextMenu = (e, data) => {
    e.preventDefault();
    handleContextMenu(e, data);
  };
  
return (
<div>
	****
	{
		arr.map(item => {
			****
			****
			<ContextMenu />
		});
	****
</div>
	
);

首先修改菜单数组,增加一个字段用来和实际内容状态判断是否需要此菜单

const menu = {
    menuId: '2',
    items: [
      { key: 1, showStatusArr: [0, 1] name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
      { key: 2, showStatusArr: [0, 3], name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
      { key: 3, showStatusArr: [2], name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
      { key: 4, showStatusArr: [4], name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
      { key: 5, showStatusArr: [0, 5], name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
      { key: 6, showStatusArr: [5], name: 'xxxx', handler: ({props}) => { xxxxx(props); }},
    ],
  };

然后直接改写ContextMenu的生成逻辑,根据条件判断来生成菜单

  ******
const ContextMenu = ({Status}) => (
    <ContentMenu id={menu.menuId}>
      {
        menu.items.map(item => {
        // 这里需要加key,不然要报错
          if(item.showStatusArr instanceof Array && item.showStatusArr.includes(Status)){
            return <Item
              key={item.key}
              onClick={item.handler}
            >
              {item.name}
            </Item>
          }
        })
      }
    </ContentMenu>
  );
  ******
return (
<div>
	...
	{
		arr.map(item => {
			****
			****
			<ContextMenu Status={item.Status} />
		});
	...
</div>	
);

此时出来的效果变成

在这里插入图片描述

因为Menu所在的DOM id是一样的,N条数据执行完之会把Menu的内容改写为最后一次渲染的,并且还打乱了Menu的布局
于是就想出了为每条记录单独创建一个id的Menu,执行useContextMenu()时动态为其指定id,这样就能保证每条记录对应的菜单栏目是事先根据状态生成好的,达到伪动态的目的
这做法缺点很明显,有几种组合,就要在右键点击时判断几种然后更新state的值,凭空增加了很多无用代码量

	// 这两个主要是用来作监听触发展开右键菜单操作的,如果是类组件,可以不用存储鼠标点击事件和点击的栏目内容
	const [rightMenuId, setRightMenuId] = useState();
	const [rightClickEve, setRightClickEve] = useState();
	// 这个用来存储右键点击的栏目内容,
	const [rightClickItem, setRightClickItem] = useState();
  
  	// 这里同时监听右键点击的对应右键菜单id和右键事件,然后去触发展开ContextMenu的方法
  	useEffect(() => {
    	if(rightMenuId){
      		handleContextMenu(rightClickEve, rightClickItem);
    	}
  	}, [rightMenuId,rightClickEve]);
  
	const handleMenu = {
    	draft: 'draftMenu',
    	submit: 'submitMenu',
    	reject: 'rejectMenu',
    	access: 'accessMenu',
    	public: 'publicMenu',
    	report: 'reportMenu',
    	items: [
      		{ key: 'edit', showStatusArr: [0,2], name: <span><EditOutlined />编辑</span>, handler: ({props}) => { rightContentClick({key: 'edit', item: {props}}) }},
      		{ key: 'submit', showStatusArr: [0,2], name: <span><CheckOutlined />提交</span>, handler: ({props}) => { rightContentClick({key: 'submit', item: {props}}) }},
      		{ key: 'delete', showStatusArr: [0,5], name: <span style={{color: 'red'}}><DeleteOutlined />删除</span>, handler: ({props}) => { rightContentClick({key: 'delete', item: {props}}) }},
      		{ key: 'rollback', showStatusArr: [1], name: <span><RollbackOutlined />撤销</span>, handler: ({props}) => { rightContentClick({key: 'rollback', item: {props}}) }},
      		{ key: 'showReason', showStatusArr: [2, 5], name: <span><EyeOutlined />查看原因</span>, handler: ({props}) => { rightContentClick({key: 'showReason', item: {props}}) }},
      		{ key: 'public', showStatusArr: [3], name: <span><NodeExpandOutlined />上架</span>, handler: ({props}) => { rightContentClick({key: 'public', item: {props}}) }},
      		{ key: 'reEdit', showStatusArr: [3], name: <span style={{color: 'red'}}><EditOutlined />重新修改</span>, handler: ({props}) => { rightContentClick({key: 'reEdit', item: {props}}) }},
      		{ key: 'cancel', showStatusArr: [4], name: <span><NodeCollapseOutlined />下架</span>, handler: ({props}) => { rightContentClick({key: 'cancel', item: {props}}) }},
      		{ key: 'update', showStatusArr: [4], name: <span><ArrowUpOutlined />升级模板</span>, handler: ({props}) => { rightContentClick({key: 'update', item: {props}}) }},
      		{ key: 'appeal', showStatusArr: [5], name: <span><SoundOutlined />申诉</span>, handler: ({props}) => { rightContentClick({key: 'appeal', item: {props}}) }},
      		{ key: 'history', showStatusArr: [0,1,2,3,4,5], name: <span style={{color: '#b99c00'}}><HistoryOutlined />操作历史</span>, handler: ({props}) => { rightContentClick({key: 'history', item: {props}}) }},
    ],
  };
  const ContextMenu = ({divId, templateStatus}) => (
    <ContentMenu id={divId}>
      {
        handleMenu.items.map(item => {
          if(item.showStatusArr instanceof Array && item.showStatusArr.includes(templateStatus)){
            return <Item
              key={item.key}
              onClick={item.handler}
            >
              {item.name}
            </Item>
          }
        })
      }
    </ContentMenu>
  );
  const { show } = useContextMenu({
    id: rightMenuId,
  });
  const handleContextMenu = (event, node) => {
    event.preventDefault();
    show(event,{
      props: node,
    });
  };
  const onRightClick = (e, item) => {
  	// 由于下面延时触发了handleContexte,不加这行会把浏览器本身右键菜单也触发了
  	e.preventDefault();
    if(item.templateStatus === 0){
      setRightMenuId(handleMenu.draft);
    }else if(item.templateStatus === 1){
      setRightMenuId(handleMenu.submit);
    }else if(item.templateStatus === 2){
      setRightMenuId(handleMenu.reject);
    }else if(item.templateStatus === 3){
      setRightMenuId(handleMenu.access);
    }else if(item.templateStatus === 4){
      setRightMenuId(handleMenu.public);
    }else if(item.templateStatus === 5){
      setRightMenuId(handleMenu.report);
    }else{
      setRightMenuId(handleMenu.draft)
    }
    // 由于state异步的问题,不能直接在右击时同步去调用,会出现双击右键才能展开的问题
    // 类组件可以直接写在state的回调函数里,这里用的函数式组件所以在上面钩子里监听变化来调用
    // handleContextMenu(e, item);
  };

  const generatorDivId = (templateStatus) => {
    if(templateStatus === 0){
      return handleMenu.draft;
    }else if(templateStatus === 1){
      return handleMenu.submit;
    }else if(templateStatus === 2){
      return handleMenu.reject;
    }else if(templateStatus === 3){
      return handleMenu.access;
    }else if(templateStatus === 4){
      return handleMenu.public;
    }else if(templateStatus === 5){
      return handleMenu.report;
    }else{
      return handleMenu.draft;
    }
  };
  return (
<div>
	...
	{
		arr.map(item => {
			<div onContextMenu={(e) => onRightClick(e, item)} title='右键点击展开操作菜单'>
			****
			****
			<ContextMenu divId={generatorDivId(item.templateStatus)} templateStatus={item.templateStatus} />
			</div>
		});
	...
</div>	
);

效果如下

在这里插入图片描述

在这里插入图片描述

方式不是很优美,不过好歹满足了要求,还是开头第一句话,如果菜单本身数量少,菜单列表也无需json格式的话采取开头文章中作者的方法更简单快捷。

原文地址:https://blog.csdn.net/niwo_345/article/details/120265756

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom