React contexify right-click menu dynamic generation

React contexify right-click menu dynamic generation

If the number of menus is small and there is no other place to share the menu list, it is recommended to refer to it The right-click menu property of react contexify disabled does not work
The processing method in this paper is relatively fast

As for why this plug-in cannot be dynamically updated according to state, the author has explained above and will not repeat it here

This method is to expand the right-click menu column as needed, and the collocation quantity increases infinitely

There are no problems with the quantity and performance of, and there are not too many tests. In my actual situation, there are six combinations, and there are not too many problems

First, add a field in the menu array to judge whether to display / disable it

This is generally the case

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 => (
        // You need to add a key here, or you will report an error
        <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>
	
);

First, modify the menu array and add a field to judge whether this menu is needed with the actual content status

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); }},
    ],
  };

Then directly rewrite the generation logic of ContextMenu and generate the menu according to condition judgment

  ******
const ContextMenu = ({Status}) => (
    <ContentMenu id={menu.menuId}>
      {
        menu.items.map(item => {
        // You need to add a key here, or you will report an error
          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>	
);

At this point, the effect becomes
Because the DOM id of the Menu is the same, after N pieces of data are executed, the contents of the Menu will be rewritten for the last rendering, and the layout of the Menu will be disturbed
So I came up with the idea of creating a Menu with an id for each record, and dynamically specifying the id when executing useContextMenu(), so as to ensure that the Menu column corresponding to each record is generated according to the state in advance, so as to achieve the purpose of pseudo dynamic
The disadvantages of this method are obvious. There are several combinations. You need to judge them when you right-click, and then update the value of state, which adds a lot of useless code out of thin air

	// These two are mainly used for monitoring and triggering the right-click menu operation. If they are class components, you don't need to store mouse click events and clicked column contents
	const [rightMenuId, setRightMenuId] = useState();
	const [rightClickEve, setRightClickEve] = useState();
	// This is used to store the right-click column content,
	const [rightClickItem, setRightClickItem] = useState();
  
  	// Here, the corresponding right-click menu id and right-click event are monitored at the same time, and then the method of expanding ContextMenu is triggered
  	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 />edit</span>, handler: ({props}) => { rightContentClick({key: 'edit', item: {props}}) }},
      		{ key: 'submit', showStatusArr: [0,2], name: <span><CheckOutlined />Submit</span>, handler: ({props}) => { rightContentClick({key: 'submit', item: {props}}) }},
      		{ key: 'delete', showStatusArr: [0,5], name: <span style={{color: 'red'}}><DeleteOutlined />delete</span>, handler: ({props}) => { rightContentClick({key: 'delete', item: {props}}) }},
      		{ key: 'rollback', showStatusArr: [1], name: <span><RollbackOutlined />revoke</span>, handler: ({props}) => { rightContentClick({key: 'rollback', item: {props}}) }},
      		{ key: 'showReason', showStatusArr: [2, 5], name: <span><EyeOutlined />View reason</span>, handler: ({props}) => { rightContentClick({key: 'showReason', item: {props}}) }},
      		{ key: 'public', showStatusArr: [3], name: <span><NodeExpandOutlined />Put on the shelf</span>, handler: ({props}) => { rightContentClick({key: 'public', item: {props}}) }},
      		{ key: 'reEdit', showStatusArr: [3], name: <span style={{color: 'red'}}><EditOutlined />Modify again</span>, handler: ({props}) => { rightContentClick({key: 'reEdit', item: {props}}) }},
      		{ key: 'cancel', showStatusArr: [4], name: <span><NodeCollapseOutlined />Off the shelf</span>, handler: ({props}) => { rightContentClick({key: 'cancel', item: {props}}) }},
      		{ key: 'update', showStatusArr: [4], name: <span><ArrowUpOutlined />Upgrade template</span>, handler: ({props}) => { rightContentClick({key: 'update', item: {props}}) }},
      		{ key: 'appeal', showStatusArr: [5], name: <span><SoundOutlined />appeal</span>, handler: ({props}) => { rightContentClick({key: 'appeal', item: {props}}) }},
      		{ key: 'history', showStatusArr: [0,1,2,3,4,5], name: <span style={{color: '#B99c00 '} > < historyoutlined / > operation history < / 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) => {
  	// Since the following delay triggers the handlecontext, not adding this line will trigger the right-click menu of the browser itself
  	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)
    }
    // Due to the asynchrony of state, you can't call it synchronously when you right-click, so you can double-click it to expand it
    // Class components can be written directly in the callback function of state. The functional components used here are called by listening for changes in the above hook
    // 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='Right click to expand the operation menu'>
			****
			****
			<ContextMenu divId={generatorDivId(item.templateStatus)} templateStatus={item.templateStatus} />
			</div>
		});
	...
</div>	
);

The effect is as follows


The method is not very beautiful, but at least it meets the requirements. It is the first sentence at the beginning. If the number of menus is small and the menu list does not need json format, it is easier and faster to adopt the method of the author in the beginning article.

Tags: Javascript React

Posted on Mon, 13 Sep 2021 14:14:35 -0400 by uidzer0