Как насчёт динамического меню когда глубина вложенности неизвестна? Пример из жизни — сайдбар. Под катом реализация дл я React.
Данные в качестве мока:
// tree.js
const tree = [
{ name: 'Условия размещения' },
{
name: 'Кто может размещать объявления',
children: [
{ name: 'Какие категории существуют?' },
{
name: 'Как добавить свое объявление',
children: [
{
name: 'Через личный кабинет',
children: [
{ name: 'Вот так добавь' }
],
},
{ name: 'Через менеджера' }
]
},
]
},
{
name: 'Сколько стоит размещение объявления',
children: [
{ name: 'Частным лицам' },
{ name: 'Организациям' },
]
}
]
export default tree
React
Создать новый проект.
$ npx create-react-app new-project
$ cd new-project
$ npm run start
Структура проекта:
.
├── src
│ ├── App.js
│ ├── App.css
│ ├── components
│ │ └── RecursiveComponent.js
│ ├── index.js
│ └── tree.js
└── public
App
- точка входа. Здесь будет жить рекурсивный компонент.
Импортируем файл мока, прогоняем с использованием компонента.
// App.js
import './App.css'
import tree from './tree'
import RecursiveComponent from './components/RecursiveComponent'
function App() {
return (
<div className="sidebar">
<ul className="sidebar-list">
{tree.map(item => (
<RecursiveComponent key={`${item.name}`} {...item} />
))}
</ul>
</div>
)
}
export default App
Внутри компонента отобразить имена элементов первого уровня. Проверить наличие вложенных
уровней и, если они есть, вернуть RecursiveComponent
снова,
передав ему новый список (рекурсия в действии).
Для того, чтобы раскрывать и закрывать активный список по нажатию на кнопку
заданы динамические стили style
и childStyle
. Состояние хранится в active
и меняестя
по клику. Для отслеживания вложенности введена переменная depth
, увеличивающая своё
значение с каждым новым шагом.
// RecursiveComponent.js
import { useState } from 'react'
const RecursiveComponent = ({ name, children, depth = 1 }) => {
const [active, setActive] = useState(false)
const style = { marginLeft: 10 + (depth * 5 + 5) }
const childStyle = { display: depth && active ? 'block' : 'none' }
const onClick = () => setActive(!active)
return (
<li className={`parent parent-${depth}`} style={style}>
<button className="button" onClick={onClick}>
<span>{name}</span>
</button>
{Array.isArray(children) ? (
<ul className="child" style={childStyle}>
{children.map(item => (
<RecursiveComponent key={item.name} depth={depth + 1} {...item} />
))}
</ul>
) : null}
</li>
)
}
export default RecursiveComponent
Напоследок простенькая стилизация:
.sidebar {
max-width: 500px;
margin: 10px auto;
}
ul {
list-style: none;
margin: 0;
padding-left: 0;
}
li {
padding: 10px;
border-left: 1px solid gainsboro;
text-align: left;
}
.button {
background-color: #28bd8b;
border: 1px solid #28bd8b;
outline: none;
font-size: 14px;
color: white;
font-weight: bold;
padding: 7px 20px;
transition: background-color .5s ease;
}
Таков итог.