H Как использовать меню или всплывающее окно для отображения компонентов в React?

#reactjs #menu

Вопрос:

У меня есть функция , которая может создавать меню самостоятельно, и я хочу использовать всплывающее меню в этих меню, как показано ниже.

 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)amp;amp;todo.completed===false).length ===0 ? null : todos.filter((todo)=>todo.categories?.includes(c.id)amp;amp;todo.completed===false).length } 
            </Typography>
            <Menu
              keepMounted
              open={popState.mouseY !== null}
              onClose={closePopCategory}
              anchorReference="anchorPosition"
              anchorPosition={
                popState.mouseY !== null amp;amp; popState.mouseX !== null
                  ? { top: popState.mouseY, left: popState.mouseX }
                  : undefined
              }
            >
            <MenuItem onClick={closePopCategory}>{c.name}</MenuItem>
            </Menu>
        </div>
          </Link>
          )
     }
  }
)} 
 

Поэтому после того , как я это сделал, когда я щелкну правой кнопкой мыши компоненты, меню между разными списками будут перекрывать друг друга , я думаю, это потому, что я использовал одно и то же состояние для управления меню, но я не могу придумать способ создания нескольких состояний или функций с отображением, и я тоже не думаю, что это отличный способ.
Каков нормальный подход, если вы хотите добавить всплывающее окно в компоненты отображения, поскольку оно управляется состоянием и функцией ?

Ответ №1:

Если вам нужно дополнительное состояние для каждого «сопоставленного» узла react, самый простой способ-поместить весь узел в его собственный компонент. Таким образом, вы бы сделали что-то вроде:

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

А затем в вашем новом компоненте обработайте состояние:

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

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

Ответ №2:

Мы можем создать отдельный компонент для 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) amp;amp; todo.completed === false
          ).length === 0
            ? null
            : todos.filter(
                (todo) =>
                  todo.categories?.includes(c.id) amp;amp; todo.completed === false
              ).length}
        </Typography>
        <Menu
          keepMounted
          open={popState.mouseY !== null}
          onClose={closePopCategory}
          anchorReference="anchorPosition"
          anchorPosition={
            popState.mouseY !== null amp;amp; 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} />;
    }
  });
}