通常菜單組件接收到的菜單數據是有父組件傳遞下來的。若是對展開對菜單進行標記,那麼菜單組件就會和父組件有交互。css
本案裏經過遞歸的方式直接在菜單組件進行數組push id 的方式進行數組對比進行判斷菜單的展開狀態。react
菜單結構樹數組
menu: [ { name: "1", id: "1", children: [ { name: "1-1", id: "1-1", children: [ ], }, { name: "2", id: "2", children: [] }, { name: "3", id: "3", children: [] }, ], }, { name: "2", id: "2", children: [] }, { name: "3", id: "3", children: [] }, ]
菜單組件less
import React, { useState } from "react"; import css from "./index.module.less"; export type MenuItem = { id: string; name: string; children: Array<MenuItem>; [propName: string]: any; }; export type MenuProps = { menu: Array<MenuItem>; }; function checkArr(arr1: Array<string>, arr2: Array<string>) { // 請確保菜單ID是惟一的,不然建議數組轉字符串進行比較 const arr3: Array<string> = []; if (arr2.length === 0) { return false; } for (var i = 0; i < arr2.length; i++) { if (arr1.indexOf(arr2[i]) > -1) { arr3.push(arr2[i]); } } if (arr3.length === arr1.length) { return true; } else { return false; } } function Menu(props: MenuProps) { const { menu } = props; const [thisPaths, setPaths] = useState([] as Array<string>); function List(menu: Array<MenuItem>, paths: Array<string>) { return menu.map((v) => { return Item(v, paths); }); } function Item(item: MenuItem, paths: Array<string>) { const newPaths: Array<string> = paths.concat(item.id); const isOPen = checkArr(newPaths, thisPaths); const hasChildren = Array.isArray(item.children) && item.children.length; return ( <div key={item.id} className={`${css.item} ${isOPen ? css.open : ""} ${ hasChildren ? css["has-children"] : "" }`} > <div className={css.title} onClick={() => { setPaths(newPaths); }} > {item.name} </div> <div className={css.children}>{List(item.children, newPaths)}</div> </div> ); } return ( <div> <div className={css.menu}>{List(menu, [])}</div> </div> ); } export default Menu;
lessthis
.menu { padding: 24px; font-size: 18px; } .item .item { padding: 6px 0 6px 24px; } .title { position: relative; padding-left: 24px; line-height: 40px; color: #5a4d40; cursor: pointer; .active { background-color: #ffdbb9; } &:hover { background-color: #f9e4d1; } .has-children > &::before { position: absolute; top: 10px; left: 7px; content: ""; border-width: 10px; border-style: solid; border-color: transparent transparent transparent #ebb684; transition: ease-out all 100ms; } } .children { display: none; } .open > .title { // background-color: #ffdbb9; &::before { top: 15px; left: 2px; transform: rotate(90deg); //border-color: #ebb684 transparent transparent transparent; } } .open > .children { display: block; }