H如何在 React 的映射组件上使用 menu 或 popover?

How to use menu or popover on mapping components in React?

我有一个可以自己创建菜单的功能,我想在这些菜单上使用弹出窗口,如下所示。

const popInitialPos = {
    mouseX: null,
    mouseY: null,
  };
  const [popState, setPopState] = React.useState<{
    mouseX: null | number;
    mouseY: null | number;
  }>(popInitialPos);

  const openPopCategory = (event: React.MouseEvent<HTMLDivElement>) => {
    event.preventDefault();
    setPopState({
      mouseX: event.clientX - 2,
      mouseY: event.clientY - 4,
    });
  };

  const closePopCategory = () => {
    setPopState(popInitialPos);
  };
//....some code
 {categories?.map( (c)=>{
          if( c.id>=5){
            return(
          <Link to={{pathname:"/list/"+c.id}} className={classes.linkItem} key={c.id}>
          <div className={classes.item} onContextMenu={(e)=>openPopCategory(e)}>
            <Dns className={classes.icon} />
            <Typography className={classes.text}>{c.name}</Typography>
            <Typography className={classes.text} style={{marginLeft:"auto"}}>
            {todos.filter((todo)=>todo.categories?.includes(c.id)&&todo.completed===false).length ===0 ? null : todos.filter((todo)=>todo.categories?.includes(c.id)&&todo.completed===false).length } 
            </Typography>
            <Menu
              keepMounted
              open={popState.mouseY !== null}
              onClose={closePopCategory}
              anchorReference="anchorPosition"
              anchorPosition={
                popState.mouseY !== null && popState.mouseX !== null
                  ? { top: popState.mouseY, left: popState.mouseX }
                  : undefined
              }
            >
            <MenuItem onClick={closePopCategory}>{c.name}</MenuItem>
            </Menu>
        </div>
          </Link>
          )
     }
  }
)} 

所以在我这样做之后,当我右键单击组件时,不同列表之间的菜单项会相互重叠,我认为这是因为我使用相同的状态来控制菜单,但我想不出一种方法来创建多个状态或功能与 mapping ,我认为这也不是一个好方法。 如果您想将弹出窗口添加到映射组件,通常的方法是什么,因为它是由状态和功能控制的?

如果每个“映射的”反应节点都需要额外的状态,最简单的方法是将整个节点放入其自己的组件中。所以你会做一些事情:

categories?.map(c => <Category key={c.id} category={c} />)

然后在您的新组件中处理状态:

function Category({ category }) {
  const [state, setState] = useState(...)

  return <Link>
     <Menu />
     ... etc
  </Link>
}

我们可以为 MenuWithPopup 创建一个单独的组件,并将状态驻留在其中。这种创建 atomic state 的方式确保 每个菜单项都有一个单独的状态。

const popInitialPos = {
  mouseX: null,
  mouseY: null,
};

const MenuWithPopup = () => {
  const [popState, setPopState] = React.useState<{
    mouseX: null | number;
    mouseY: null | number;
  }>(popInitialPos);

  const openPopCategory = (event: React.MouseEvent<HTMLDivElement>) => {
    event.preventDefault();
    setPopState({
      mouseX: event.clientX - 2,
      mouseY: event.clientY - 4,
    });
  };

  const closePopCategory = () => {
    setPopState(popInitialPos);
  };

  return (
    <Link
      to={{pathname: '/list/' + c.id}}
      className={classes.linkItem}
      key={c.id}
    >
      <div className={classes.item} onContextMenu={(e) => openPopCategory(e)}>
        <Dns className={classes.icon} />
        <Typography className={classes.text}>{c.name}</Typography>
        <Typography className={classes.text} style={{marginLeft: 'auto'}}>
          {todos.filter(
            (todo) =>
              todo.categories?.includes(c.id) && todo.completed === false
          ).length === 0
            ? null
            : todos.filter(
                (todo) =>
                  todo.categories?.includes(c.id) && todo.completed === false
              ).length}
        </Typography>
        <Menu
          keepMounted
          open={popState.mouseY !== null}
          onClose={closePopCategory}
          anchorReference="anchorPosition"
          anchorPosition={
            popState.mouseY !== null && popState.mouseX !== null
              ? {top: popState.mouseY, left: popState.mouseX}
              : undefined
          }
        >
          <MenuItem onClick={closePopCategory}>{c.name}</MenuItem>
        </Menu>
      </div>
    </Link>
  );
};

现在我们可以在地图中使用这个组件了

{
  categories?.map((c) => {
    if (c.id >= 5) {
      return <MenuItemWithPopup {pass the necessary props here} />;
    }
  });
}