#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} />;
}
});
}