在递归组件中删除孙子时出现问题



我一直在努力实现什么

创建一个由配置驱动的嵌套上下文菜单。


我被困在哪里:

子菜单呈现正确,但如果有两个以上级别,根级别的更改只影响其子菜单,而不会影响其整个树

下面是沙箱链接供您检查。


复制步骤:

  1. 加载时,会显示一个菜单(比如菜单(
  2. 单击文件,它将打开其子菜单(比如子菜单1(
  3. 单击子菜单1中的打开,再次打开另一个子菜单(如子菜单2(
  4. 现在,当您单击菜单中的编辑时,子菜单1会消失,但子菜单2不会消失

我想,我知道这个问题。子菜单2不刷新,因为道具或状态未更改。为了隐藏它,我们需要慢慢滴下一些道具,但如果没有国家管理系统,我们想不出优雅的方法来做到这一点。

如果ContextMenu组件负责状态管理,并且递归被扁平化为迭代,那么您会过得更好。

function ContextItem({ item, onClick }) {
return (
<div className="menu-item" onClick={() => onClick(item)}>
<p className="menu-title">{item.title}</p>
{item.children && item.children.length > 0 ? <i className="right-icon">{">"}</i> : null}
</div>
);
}
function MenuList({ list, onClick }) {
return (
<div className="menu-container">
{list.map((listItem) => (
<ContextItem item={listItem} key={listItem.title} onClick={onClick} />
))}
</div>
);
}
const ContextMenu = ({ list }) => {
const [openSubmenus, setOpenSubmenus] = React.useState([]);
const clickHandler = React.useCallback((item, level) => {
if (item.children && item.children.length) {
setOpenSubmenus((oldItems) => {
return [...oldItems.slice(0, level), item.children];
});
} else {
setOpenSubmenus([]); // item selected, clear submenus
alert(item.title);
}
}, []);
const menus = [list, ...openSubmenus];
return (
<div className="menu">
{menus.map((menu, level) => (
<MenuList
key={level}
list={menu}
level={level}
onClick={(item) => clickHandler(item, level)}
/>
))}
</div>
);
};
const menuList = [{
title: "File",
children: [{
title: "Close",
children: [],
action: "fileClose",
}, {
title: "Open",
children: [{
title: "A:\",
children: [],
action: "",
}, {
title: "C:\",
children: [],
action: "",
}, {
title: "\",
children: [],
action: "",
}],
action: "",
}, {
title: "Find",
children: [{
title: "here",
children: [],
}, {
title: "elsewhere",
children: [],
}],
action: "",
}, {
title: "Backup",
children: [],
action: "backup",
}],
action: "",
}, {
title: "Edit",
children: [],
action: "edit",
}];
function App() {
return <ContextMenu list={menuList} />;
}
ReactDOM.render(<App />, document.getElementById("root"));
.menu {
display: flex;
flex-direction: row;
}
.menu-container {
display: flex;
flex-direction: column;
background-color: #eee;
border: 1px solid gray;
border-radius: 4px;
}
.menu-item {
display: flex;
flex-direction: row;
margin: 2px;
max-width: 200px;
line-height: 30px;
padding: 5px 10px;
}
.menu-title {
min-width: 80px;
height: 30px;
flex-grow: 1;
margin: 0;
vertical-align: middle;
}
.menu-title.active {
background-color: blue;
color: white;
}
.right-icon {
width: 25px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.0.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

当selectedItem发生更改时,您可以使用标签作为键来重置ContextMenu状态(假设标签在给定深度上是唯一的,但这似乎是合理的,否则您可以添加唯一的id(。

export const ContextMenu = ({list}) => {
const [selectedItem, setSelectedItem] = useState();
return (
<div className="menu">
<div className="menu-container">
{list.map((listItem) => {
return (
<ContextItem
item={listItem}
key={listItem.title}
onClick={setSelectedItem}
/>
);
})}
</div>
{selectedItem?.children.length > 0 && <ContextMenu
key={selectedItem.title}
list={selectedItem.children}/>}
</div>
);
};

最新更新