react-contexify icon indicating copy to clipboard operation
react-contexify copied to clipboard

Dynamically generate Items for Menu

Open Xuroth opened this issue 3 years ago • 5 comments

Do you want to request a feature or report a bug? This may be a feature request, or I may simply be missing something. What is the current behavior? Currently, you have to explicitly define the specific Items to render as JSX components. I'm attempting to get the passed props in a component so I can render a dynamic list.

To clarify, I am trying to use this in MySeparateComponent:

const { show } = useContextMenu({
  id: 'my-separate-component'
 });
 
 //...
 
 const onContextClick = (ev) => {
  show(ev, {
    items: [itemA, itemB, itemC,]
  })
}

And then I want to access the items array in the context menu to generate the list of Items:

function MyCustomContextMenu(props) {

  const handleItemClick = (itemClicked) => {
    //... (Do something with the item that was passed from props)
  }

  return (
    <Menu
      id="my-separate-component"
    >
      {props.items.map(( item, index ) => <Item onClick={() => handleItemClick(item)} key={index}>{item.name}</Item>)}
    </Menu>
}

Am I missing something? Or is this outside the scope of this library?

Xuroth avatar Nov 18 '21 12:11 Xuroth

The props are passed to the onClick. I solved this w mobx by having MyCustomContextMenu fetch items from observable state.

eirikhm avatar Nov 19 '21 08:11 eirikhm

Hmm... interesting. I use a combination of redux and context (redux handles app data, context is for passing derived data (from expensive calculations)).

I'll look into creating a context for this. Thanks for your help @eirikhm!!

EDIT: I'll close this issue after I've had some time to build and test.

Xuroth avatar Nov 19 '21 09:11 Xuroth

@Xuroth can you post your final solution. I face the same problem.

thx

DrStriky avatar Nov 23 '21 17:11 DrStriky

Yeah. This feature is exactly what we need ❤️

galexandrade avatar Nov 26 '21 16:11 galexandrade

I solved using a React.Children because the limitation of use ReactFragment

That's the code:

ContextMenu

import { useMemo } from 'react';
import { Menu, Item, Separator, Submenu, ItemParams } from 'react-contexify';
import 'react-contexify/dist/ReactContexify.css';

export type ContextMenuItem = {
  label: string;
  subMenuItems?: ContextMenuItem[];
  hasSubmenu?: boolean;
  disabled?: boolean;
  handleItemClick: (args: ItemParams<any, any>) => void;
};
type Props = {
  menuId: string;
  items: ContextMenuItem[];
};

export default function ContextMenu({ menuId, items }: Props) {
  const children = useMemo(() => {
    const elements: React.ReactNode[] = [];
    for (let index = 0; index < items.length; index++) {
      const item = items[index];
      elements.push(
        <Item
          key={item.label}
          onClick={item.handleItemClick}
          disabled={item.disabled}
        >
          {item.label}
        </Item>,
      );
      if (index !== items.length - 1) elements.push(<Separator />);
      if (item.hasSubmenu)
        elements.push(
          <Submenu label={item.label}>
            {item.subMenuItems?.map((subMenuItem) => (
              <Item
                key={subMenuItem.label}
                onClick={subMenuItem.handleItemClick}
                disabled={subMenuItem.disabled}
              >
                {subMenuItem.label}
              </Item>
            ))}
          </Submenu>,
        );
    }

    return elements;
  }, []);

  return <Menu id={menuId}>{children}</Menu>;
}

wilmerterrero avatar Mar 17 '22 21:03 wilmerterrero