Рекурсия и js-фреймворки

Как насчёт динамического меню когда глубина вложенности неизвестна? Пример из жизни сайдбар. Под катом реализация для React и Vue.

В качестве мока используем данные из trees.js.

// tree.js
const tree = [
  { name: 'Level 1.1' },
  {
    name: 'Level 1.2',
    children: [
      { name: 'Level 2.1' },
      { name: 'Level 2.2' }
    ]
  },
  {
    name: 'Level 1.3',
    children: [
      { name: 'Level 2.3' },
      { name: 'Level 2.4' },
      {
        name: 'Level 2.5',
        children: [
          { name: 'Level 3.1' },
          {
            name: 'Level 3.2',
            children: [
              { name: 'Level 4.1' },
              { name: 'Level 4.2' }
            ]
          },
          { name: 'Level 3.3' }
        ]
      }
    ]
  }
]

export default tree

React

Создать новый проект.

$ npx create-react-app new-project
$ cd new-project
$ npm run start

Структура проекта:

.
├── src
│   ├── App.js
│   ├── components
│   │   ├── Sidebar.js
│   │   └── SidebarItem.js
│   ├── index.js
│   └── tree.js
└── public

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

import React from 'react'
import tree from './tree'
import Sidebar from './components/Sidebar'

function handleClick (e) {
  e.preventDefault();
  // При клике на любой пункт вывести название пункта
  console.log(e.target.textContent)
}

export default function App() {
  return <Sidebar items={tree} handleClick={handleClick} />
}

Vue

Vue отличается от React своей магией. Не всегда очевидно то, что фреймворк хранит «под капотом», но порой эта магия может упростить жизнь. Но не в этом случае. Логика аналогична той, что мы писали выше.

Создадим новый проект. Для простоты сделаем это с vue-cli.

$ npx @vue/cli create new-project
$ cd new-project
$ npm run serve

Структура проекта:

.
├── public
└── src
    ├── App.vue
    ├── components
    │   ├── Sidebar.vue
    │   └── SidebarItem.vue
    ├── main.js
    └── tree.js

Точка входа. Подключим компоненты и прокинем данные.

<template>
  <div id="app">
    <sidebar :data="tree" @handleClick="handleClick" />
  </div>
</template>

<script>
import tree from './tree'
import Sidebar from './components/Sidebar'

export default {
  name: 'app',
  data () {
    return {
      tree
    }
  },
  components: {
    Sidebar
  },
  methods: {
    handleClick (node) {
      console.log('Clicked: ', node.name)
    }
  }
}
</script>

Recursion